From a858393b0ce5c330bda466e5ae3a658ca98588ae Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 16 Sep 2011 16:26:30 +0300 Subject: Bluetooth: EFS: l2cap extended feature mask update Update L2CAP extended feature mask to reflect recent BT spec. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index ab90ae0970a6..293376750dc4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -91,13 +91,17 @@ struct l2cap_conninfo { #define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 #define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 -/* L2CAP feature mask */ +/* L2CAP extended feature mask */ #define L2CAP_FEAT_FLOWCTL 0x00000001 #define L2CAP_FEAT_RETRANS 0x00000002 +#define L2CAP_FEAT_BIDIR_QOS 0x00000004 #define L2CAP_FEAT_ERTM 0x00000008 #define L2CAP_FEAT_STREAMING 0x00000010 #define L2CAP_FEAT_FCS 0x00000020 +#define L2CAP_FEAT_EXT_FLOW 0x00000040 #define L2CAP_FEAT_FIXED_CHAN 0x00000080 +#define L2CAP_FEAT_EXT_WINDOW 0x00000100 +#define L2CAP_FEAT_UCD 0x00000200 /* L2CAP checksum option */ #define L2CAP_FCS_NONE 0x00 -- cgit v1.2.3 From a5fd6f300433ef7458c6d934f81f47ebd7c7e805 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Fri, 16 Sep 2011 16:26:32 +0300 Subject: Bluetooth: EFS: add enable_hs kernel param Add enable_hs kernel parameter. L2CAP_FEAT_EXT_FLOW depends on it. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8cd12917733b..3158cec9e274 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -57,6 +57,7 @@ #include int disable_ertm; +int enable_hs; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; static u8 l2cap_fixed_chan[8] = { 0x02, }; @@ -2782,6 +2783,9 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm if (!disable_ertm) feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; + if (enable_hs) + feat_mask |= L2CAP_FEAT_EXT_FLOW; + put_unaligned_le32(feat_mask, rsp->data); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); @@ -4306,3 +4310,6 @@ void l2cap_exit(void) module_param(disable_ertm, bool, 0644); MODULE_PARM_DESC(disable_ertm, "Disable enhanced retransmission mode"); + +module_param(enable_hs, bool, 0644); +MODULE_PARM_DESC(enable_hs, "Enable High Speed"); -- cgit v1.2.3 From 794d175698f0e78be7f2e3f4bdbe0e7cd3f2d6ae Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Fri, 26 Aug 2011 14:06:02 +0200 Subject: Bluetooth: hidp: Stop I/O on shutdown Current hidp driver purges the in/out queue on HID shutdown, but does not prevent further I/O. If a driver uses hidp_output_raw_report or hidp_get_raw_report during shutdown, the driver hangs for 5 or 10 seconds per call until it gets a timeout. That is, if the output queue of an HID driver has 10 messages pending, it will take 50s until hid_destroy_device() will return. The hidp_session_sem semaphore is held during shutdown so no other HID device may be added/removed during this time. This patch makes hidp_output_raw_report and hidp_get_raw_report fail if session->terminate is true. Also hidp_session will wakeup all current calls to these functions to cancel the current operations. We already purge the current I/O queues on hidp_stop(), so this data loss does not change the behaviour of the HID drivers. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 075a3e920caf..d7bae2be83b2 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -255,6 +255,9 @@ static int __hidp_send_ctrl_message(struct hidp_session *session, BT_DBG("session %p data %p size %d", session, data, size); + if (atomic_read(&session->terminate)) + return -EIO; + skb = alloc_skb(size + 1, GFP_ATOMIC); if (!skb) { BT_ERR("Can't allocate memory for new frame"); @@ -329,6 +332,7 @@ static int hidp_get_raw_report(struct hid_device *hid, struct sk_buff *skb; size_t len; int numbered_reports = hid->report_enum[report_type].numbered; + int ret; switch (report_type) { case HID_FEATURE_REPORT: @@ -352,8 +356,9 @@ static int hidp_get_raw_report(struct hid_device *hid, session->waiting_report_number = numbered_reports ? report_number : -1; set_bit(HIDP_WAITING_FOR_RETURN, &session->flags); data[0] = report_number; - if (hidp_send_ctrl_message(hid->driver_data, report_type, data, 1)) - goto err_eio; + ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, 1); + if (ret) + goto err; /* Wait for the return of the report. The returned report gets put in session->report_return. */ @@ -365,11 +370,13 @@ static int hidp_get_raw_report(struct hid_device *hid, 5*HZ); if (res == 0) { /* timeout */ - goto err_eio; + ret = -EIO; + goto err; } if (res < 0) { /* signal */ - goto err_restartsys; + ret = -ERESTARTSYS; + goto err; } } @@ -390,14 +397,10 @@ static int hidp_get_raw_report(struct hid_device *hid, return len; -err_restartsys: - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); - mutex_unlock(&session->report_mutex); - return -ERESTARTSYS; -err_eio: +err: clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); mutex_unlock(&session->report_mutex); - return -EIO; + return ret; } static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, size_t count, @@ -422,11 +425,10 @@ static int hidp_output_raw_report(struct hid_device *hid, unsigned char *data, s /* Set up our wait, and send the report request to the device. */ set_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); - if (hidp_send_ctrl_message(hid->driver_data, report_type, - data, count)) { - ret = -ENOMEM; + ret = hidp_send_ctrl_message(hid->driver_data, report_type, data, + count); + if (ret) goto err; - } /* Wait for the ACK from the device. */ while (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { @@ -739,6 +741,10 @@ static int hidp_session(void *arg) remove_wait_queue(sk_sleep(intr_sk), &intr_wait); remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); + clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + wake_up_interruptible(&session->report_queue); + down_write(&hidp_session_sem); hidp_del_timer(session); -- cgit v1.2.3 From cd11cdd28468d6222ce6489b2212fa7b0efaefdf Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 17:35:31 -0300 Subject: Bluetooth: use list_for_each_entry() in hidp list_for_each_entry is much more meaningful. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index d7bae2be83b2..08bfbb0f0926 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -81,12 +81,10 @@ static unsigned char hidp_mkeyspat[] = { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }; static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) { struct hidp_session *session; - struct list_head *p; BT_DBG(""); - list_for_each(p, &hidp_session_list) { - session = list_entry(p, struct hidp_session, list); + list_for_each_entry(session, &hidp_session_list, list) { if (!bacmp(bdaddr, &session->bdaddr)) return session; } @@ -1140,19 +1138,16 @@ int hidp_del_connection(struct hidp_conndel_req *req) int hidp_get_connlist(struct hidp_connlist_req *req) { - struct list_head *p; + struct hidp_session *session; int err = 0, n = 0; BT_DBG(""); down_read(&hidp_session_sem); - list_for_each(p, &hidp_session_list) { - struct hidp_session *session; + list_for_each_entry(session, &hidp_session_list, list) { struct hidp_conninfo ci; - session = list_entry(p, struct hidp_session, list); - __hidp_copy_session(session, &ci); if (copy_to_user(req->ci, &ci, sizeof(ci))) { -- cgit v1.2.3 From dc0da5cdac44aea6a04b18acc5526931430e0c35 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 18:02:13 -0300 Subject: Bluetooth: prioritize the interrupt channel in hidp Interrupt channel has low latency requiments, should be processed first. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 08bfbb0f0926..2694a0a6186d 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -714,18 +714,18 @@ static int hidp_session(void *arg) intr_sk->sk_state != BT_CONNECTED) break; - while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { + while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) - hidp_recv_ctrl_frame(session, skb); + hidp_recv_intr_frame(session, skb); else kfree_skb(skb); } - while ((skb = skb_dequeue(&intr_sk->sk_receive_queue))) { + while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) - hidp_recv_intr_frame(session, skb); + hidp_recv_ctrl_frame(session, skb); else kfree_skb(skb); } -- cgit v1.2.3 From 679344e44e09c74848535a28948df7c3b3c1a071 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 20:51:37 -0300 Subject: Bluetooth: Trasmit interrupt channel messages first interrupt channel is low latency. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 2694a0a6186d..6cb1d9e8eac5 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -663,25 +663,32 @@ static int hidp_send_frame(struct socket *sock, unsigned char *data, int len) return kernel_sendmsg(sock, &msg, &iv, 1, len); } -static void hidp_process_transmit(struct hidp_session *session) +static void hidp_process_intr_transmit(struct hidp_session *session) { struct sk_buff *skb; BT_DBG("session %p", session); - while ((skb = skb_dequeue(&session->ctrl_transmit))) { - if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->ctrl_transmit, skb); + while ((skb = skb_dequeue(&session->intr_transmit))) { + if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { + skb_queue_head(&session->intr_transmit, skb); break; } hidp_set_timer(session); kfree_skb(skb); } +} - while ((skb = skb_dequeue(&session->intr_transmit))) { - if (hidp_send_frame(session->intr_sock, skb->data, skb->len) < 0) { - skb_queue_head(&session->intr_transmit, skb); +static void hidp_process_ctrl_transmit(struct hidp_session *session) +{ + struct sk_buff *skb; + + BT_DBG("session %p", session); + + while ((skb = skb_dequeue(&session->ctrl_transmit))) { + if (hidp_send_frame(session->ctrl_sock, skb->data, skb->len) < 0) { + skb_queue_head(&session->ctrl_transmit, skb); break; } @@ -722,6 +729,8 @@ static int hidp_session(void *arg) kfree_skb(skb); } + hidp_process_intr_transmit(session); + while ((skb = skb_dequeue(&ctrl_sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) @@ -730,7 +739,7 @@ static int hidp_session(void *arg) kfree_skb(skb); } - hidp_process_transmit(session); + hidp_process_ctrl_transmit(session); schedule(); set_current_state(TASK_INTERRUPTIBLE); -- cgit v1.2.3 From 3415a5fdde715062aa18bd6361f8e75cfbb31208 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 21:17:32 -0300 Subject: Bluetooth: Fix input device registration This is a regression fix. It made impossible use input device when hid fails. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 6cb1d9e8eac5..8d99a17c478c 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -812,7 +812,7 @@ static int hidp_setup_input(struct hidp_session *session, struct hidp_connadd_req *req) { struct input_dev *input; - int err, i; + int i; input = input_allocate_device(); if (!input) @@ -859,13 +859,6 @@ static int hidp_setup_input(struct hidp_session *session, input->event = hidp_input_event; - err = input_register_device(input); - if (err < 0) { - input_free_device(input); - session->input = NULL; - return err; - } - return 0; } @@ -1067,7 +1060,11 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, !session->waiting_for_startup); } - err = hid_add_device(session->hid); + if (session->hid) + err = hid_add_device(session->hid); + else + err = input_register_device(session->input); + if (err < 0) { atomic_inc(&session->terminate); wake_up_process(session->task); -- cgit v1.2.3 From 192893c72197b3f49f76a315024bdcd5aa26fff3 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 21:27:56 -0300 Subject: Bluetooth: Remove wrong error check d458a9dfc add this check, but now it proves to be wrong. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 8d99a17c478c..7722d8346684 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -1023,7 +1023,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, if (req->rd_size > 0) { err = hidp_setup_hid(session, req); - if (err && err != -ENODEV) + if (err) goto purge; } -- cgit v1.2.3 From ab88f7149e0e3284623adcaa60460a9a69d9f131 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 22:05:37 -0300 Subject: Bluetooth: Uses test_and_clear_bit() when possible Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 7722d8346684..f2bec4981b76 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -496,10 +496,9 @@ static void hidp_process_handshake(struct hidp_session *session, case HIDP_HSHK_ERR_INVALID_REPORT_ID: case HIDP_HSHK_ERR_UNSUPPORTED_REQUEST: case HIDP_HSHK_ERR_INVALID_PARAMETER: - if (test_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) { - clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags); + if (test_and_clear_bit(HIDP_WAITING_FOR_RETURN, &session->flags)) wake_up_interruptible(&session->report_queue); - } + /* FIXME: Call into SET_ GET_ handlers here */ break; @@ -520,10 +519,8 @@ static void hidp_process_handshake(struct hidp_session *session, } /* Wake up the waiting thread. */ - if (test_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) { - clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags); + if (test_and_clear_bit(HIDP_WAITING_FOR_SEND_ACK, &session->flags)) wake_up_interruptible(&session->report_queue); - } } static void hidp_process_hid_control(struct hidp_session *session, -- cgit v1.2.3 From 1785dbf9e30be62ab45e34900e574b8307bc98b5 Mon Sep 17 00:00:00 2001 From: Peter Hurley Date: Tue, 30 Aug 2011 11:53:35 -0400 Subject: Bluetooth: hidp: safely acquire hci connection Claim device lock to safely enumerate hci connection list and bump hci connection proxy device ref count simultaneously. This patch incorporates David Herrmann's fix to prevent adding an HID device when the hci connection no longer exists. Signed-off-by: David Herrmann Signed-off-by: Peter Hurley Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index f2bec4981b76..304a73f96cc2 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -95,8 +95,6 @@ static void __hidp_link_session(struct hidp_session *session) { __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); - - hci_conn_hold_device(session->conn); } static void __hidp_unlink_session(struct hidp_session *session) @@ -785,24 +783,26 @@ static int hidp_session(void *arg) return 0; } -static struct device *hidp_get_device(struct hidp_session *session) +static struct hci_conn *hidp_find_connection(struct hidp_session *session) { bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; - struct device *device = NULL; + struct hci_conn *conn; struct hci_dev *hdev; hdev = hci_get_route(dst, src); if (!hdev) return NULL; - session->conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); - if (session->conn) - device = &session->conn->dev; + hci_dev_lock_bh(hdev); + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst); + if (conn) + hci_conn_hold_device(conn); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return device; + return conn; } static int hidp_setup_input(struct hidp_session *session, @@ -852,7 +852,7 @@ static int hidp_setup_input(struct hidp_session *session, input->relbit[0] |= BIT_MASK(REL_WHEEL); } - input->dev.parent = hidp_get_device(session); + input->dev.parent = &session->conn->dev; input->event = hidp_input_event; @@ -952,7 +952,7 @@ static int hidp_setup_hid(struct hidp_session *session, strncpy(hid->phys, batostr(&bt_sk(session->ctrl_sock->sk)->src), 64); strncpy(hid->uniq, batostr(&bt_sk(session->ctrl_sock->sk)->dst), 64); - hid->dev.parent = hidp_get_device(session); + hid->dev.parent = &session->conn->dev; hid->ll_driver = &hidp_hid_driver; hid->hid_get_raw_report = hidp_get_raw_report; @@ -993,6 +993,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, goto failed; } + session->conn = hidp_find_connection(session); + if (!session->conn) { + err = -ENOTCONN; + goto failed; + } + bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, @@ -1018,6 +1024,8 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->flags = req->flags & (1 << HIDP_BLUETOOTH_VENDOR_ID); session->idle_to = req->idle_to; + __hidp_link_session(session); + if (req->rd_size > 0) { err = hidp_setup_hid(session, req); if (err) @@ -1030,8 +1038,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, goto purge; } - __hidp_link_session(session); - hidp_set_timer(session); if (session->hid) { @@ -1084,8 +1090,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, unlink: hidp_del_timer(session); - __hidp_unlink_session(session); - if (session->input) { input_unregister_device(session->input); session->input = NULL; @@ -1100,6 +1104,8 @@ unlink: session->rd_data = NULL; purge: + __hidp_unlink_session(session); + skb_queue_purge(&session->ctrl_transmit); skb_queue_purge(&session->intr_transmit); -- cgit v1.2.3 From 81b25cd04387fbceb76fe893db4863a380941413 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 6 Oct 2011 23:32:29 -0300 Subject: Bluetooth: Delay session allocation in hidp It gets allocated only when it is really needed. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 304a73f96cc2..fbbf80295020 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -979,18 +979,20 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, bacmp(&bt_sk(ctrl_sock->sk)->dst, &bt_sk(intr_sock->sk)->dst)) return -ENOTUNIQ; - session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); - if (!session) - return -ENOMEM; - BT_DBG("rd_data %p rd_size %d", req->rd_data, req->rd_size); down_write(&hidp_session_sem); s = __hidp_get_session(&bt_sk(ctrl_sock->sk)->dst); if (s && s->state == BT_CONNECTED) { - err = -EEXIST; - goto failed; + up_write(&hidp_session_sem); + return -EEXIST; + } + + session = kzalloc(sizeof(struct hidp_session), GFP_KERNEL); + if (!session) { + up_write(&hidp_session_sem); + return -ENOMEM; } session->conn = hidp_find_connection(session); -- cgit v1.2.3 From 3e90dc86f4b840297bd1fafdb9ba1bf58f2e0e49 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 7 Oct 2011 01:29:51 -0300 Subject: Bluetooth: Rename hidp_find_connection() hidp_get_connection() makes more sense because we hold a reference to the connection inside this function. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index fbbf80295020..217ef4761560 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -783,7 +783,7 @@ static int hidp_session(void *arg) return 0; } -static struct hci_conn *hidp_find_connection(struct hidp_session *session) +static struct hci_conn *hidp_get_connection(struct hidp_session *session) { bdaddr_t *src = &bt_sk(session->ctrl_sock->sk)->src; bdaddr_t *dst = &bt_sk(session->ctrl_sock->sk)->dst; @@ -995,7 +995,7 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, return -ENOMEM; } - session->conn = hidp_find_connection(session); + session->conn = hidp_get_connection(session); if (!session->conn) { err = -ENOTCONN; goto failed; -- cgit v1.2.3 From 5a9b80e2cd993f77d6d068470a4fd77fdfae44ab Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sun, 9 Oct 2011 12:12:16 +0200 Subject: Bluetooth: btusb: also be quiet when suspending usb_submit_urb() returns -ENODEV when a usb device is disconnected. In commit 4935f1c164ac528dff3538f97953b385ba500710 ("Bluetooth: btusb: be quiet on device disconnect") I stopped treating that return as an error in the three btusb_*_complete() functions. It turns out btusb_send_frame() generates a similar error if the system is suspended while the bluetooth usb device is enabled. The sensible thing to do here seems to be to treat -ENODEV (and -EPERM) just like the btusb_*_complete() functions now do. Signed-off-by: Paul Bolle Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btusb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 675246a6f7ef..18fde3be530f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -767,7 +767,9 @@ skip_waking: err = usb_submit_urb(urb, GFP_ATOMIC); if (err < 0) { - BT_ERR("%s urb %p submission failed", hdev->name, urb); + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", + hdev->name, urb, -err); kfree(urb->setup_packet); usb_unanchor_urb(urb); } else { -- cgit v1.2.3 From d4b8d1c9c1564f4cbce86cbbee099fadf735b226 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Sun, 9 Oct 2011 12:12:22 +0200 Subject: Bluetooth: btusb: hide more usb_submit_urb errors There are still three calls of usb_submit_urb() that will print errors if those calls return -EPERM or -ENODEV. I have never triggered these, so I'm not sure when these return values might be seen. It still makes sense to be silent if these occur (since "urb is being killed" and "device got disconnected" aren't things to worry about). Signed-off-by: Paul Bolle Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btusb.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 18fde3be530f..abfc4ee87ee8 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -312,7 +312,8 @@ static int btusb_submit_intr_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } @@ -397,7 +398,8 @@ static int btusb_submit_bulk_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } @@ -520,7 +522,8 @@ static int btusb_submit_isoc_urb(struct hci_dev *hdev, gfp_t mem_flags) err = usb_submit_urb(urb, mem_flags); if (err < 0) { - BT_ERR("%s urb %p submission failed (%d)", + if (err != -EPERM && err != -ENODEV) + BT_ERR("%s urb %p submission failed (%d)", hdev->name, urb, -err); usb_unanchor_urb(urb); } -- cgit v1.2.3 From d57b0e8b8990419b7b7ae0dda5cc4452720b3c7c Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 14:04:31 +0300 Subject: Bluetooth: convert flushable variable to flag in l2cap chan flushable variable inside l2cap_chan is a logical one and can be easily converted to flag. Added flags in l2cap_chan structure. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 7 ++++++- net/bluetooth/l2cap_core.c | 3 ++- net/bluetooth/l2cap_sock.c | 12 ++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 293376750dc4..0fe5d59b71a3 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -326,7 +326,6 @@ struct l2cap_chan { __u8 sec_level; __u8 role_switch; __u8 force_reliable; - __u8 flushable; __u8 force_active; __u8 ident; @@ -346,6 +345,7 @@ struct l2cap_chan { unsigned long conf_state; unsigned long conn_state; + unsigned long flags; __u8 next_tx_seq; __u8 expected_ack_seq; @@ -463,6 +463,11 @@ enum { CONN_RNR_SENT, }; +/* Definitions for flags in l2cap_chan */ +enum { + FLAG_FLUSHABLE, +}; + #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) #define __clear_chan_timer(c) l2cap_clear_timer(c, &c->chan_timer) #define __set_retrans_timer(c) l2cap_set_timer(c, &c->retrans_timer, \ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3158cec9e274..b21ecfffcaa9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1253,7 +1253,8 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len); - if (!chan->flushable && lmp_no_flush_capable(hcon->hdev)) + if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && + lmp_no_flush_capable(hcon->hdev)) flags = ACL_START_NO_FLUSH; else flags = ACL_START; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 61f1f623091d..99782cb4f0b4 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -446,7 +446,8 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; case BT_FLUSHABLE: - if (put_user(chan->flushable, (u32 __user *) optval)) + if (put_user(test_bit(FLAG_FLUSHABLE, &chan->flags), + (u32 __user *) optval)) err = -EFAULT; break; @@ -655,7 +656,10 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch } } - chan->flushable = opt; + if (opt) + set_bit(FLAG_FLUSHABLE, &chan->flags); + else + clear_bit(FLAG_FLUSHABLE, &chan->flags); break; case BT_POWER: @@ -931,7 +935,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->sec_level = pchan->sec_level; chan->role_switch = pchan->role_switch; chan->force_reliable = pchan->force_reliable; - chan->flushable = pchan->flushable; + chan->flags = pchan->flags; chan->force_active = pchan->force_active; } else { @@ -962,7 +966,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->sec_level = BT_SECURITY_LOW; chan->role_switch = 0; chan->force_reliable = 0; - chan->flushable = BT_FLUSHABLE_OFF; + chan->flags = 0; chan->force_active = BT_POWER_FORCE_ACTIVE_ON; } -- cgit v1.2.3 From ecf61bdba845b5e77cf1d5e8620ef54abcfa50ef Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 14:04:32 +0300 Subject: Bluetooth: convert force_reliable variable to flag in l2cap chan force_reliable variable inside l2cap_chan is a logical one and can be easily converted to flag Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 2 +- net/bluetooth/l2cap_sock.c | 10 ++++++---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0fe5d59b71a3..6c0d247de94c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -325,7 +325,6 @@ struct l2cap_chan { __u8 sec_level; __u8 role_switch; - __u8 force_reliable; __u8 force_active; __u8 ident; @@ -465,6 +464,7 @@ enum { /* Definitions for flags in l2cap_chan */ enum { + FLAG_FORCE_RELIABLE, FLAG_FLUSHABLE, }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index b21ecfffcaa9..57e4b2cf7b6a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -948,7 +948,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err) list_for_each_entry(chan, &conn->chan_l, list) { struct sock *sk = chan->sk; - if (chan->force_reliable) + if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) sk->sk_err = err; } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 99782cb4f0b4..405d736131e2 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -359,7 +359,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us if (chan->role_switch) opt |= L2CAP_LM_MASTER; - if (chan->force_reliable) + if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) opt |= L2CAP_LM_RELIABLE; if (put_user(opt, (u32 __user *) optval)) @@ -550,7 +550,11 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->sec_level = BT_SECURITY_HIGH; chan->role_switch = (opt & L2CAP_LM_MASTER); - chan->force_reliable = (opt & L2CAP_LM_RELIABLE); + + if (opt & L2CAP_LM_RELIABLE) + set_bit(FLAG_FORCE_RELIABLE, &chan->flags); + else + clear_bit(FLAG_FORCE_RELIABLE, &chan->flags); break; default: @@ -934,7 +938,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->tx_win = pchan->tx_win; chan->sec_level = pchan->sec_level; chan->role_switch = pchan->role_switch; - chan->force_reliable = pchan->force_reliable; chan->flags = pchan->flags; chan->force_active = pchan->force_active; } else { @@ -965,7 +968,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; chan->sec_level = BT_SECURITY_LOW; chan->role_switch = 0; - chan->force_reliable = 0; chan->flags = 0; chan->force_active = BT_POWER_FORCE_ACTIVE_ON; -- cgit v1.2.3 From 15770b1ab9747de47604da3494e187056b120aff Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 14:04:33 +0300 Subject: Bluetooth: convert force_active variable to flag in l2cap chan force_active variable inside l2cap_chan is a logical one and can be easily converted to flag Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 4 ++-- net/bluetooth/l2cap_sock.c | 12 +++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6c0d247de94c..440e7b86c01d 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -325,7 +325,6 @@ struct l2cap_chan { __u8 sec_level; __u8 role_switch; - __u8 force_active; __u8 ident; @@ -464,6 +463,7 @@ enum { /* Definitions for flags in l2cap_chan */ enum { + FLAG_FORCE_ACTIVE, FLAG_FORCE_RELIABLE, FLAG_FLUSHABLE, }; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 57e4b2cf7b6a..aeeacf8076d1 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -605,7 +605,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) else flags = ACL_START; - bt_cb(skb)->force_active = chan->force_active; + bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); hci_send_acl(chan->conn->hcon, skb, flags); } @@ -1259,7 +1259,7 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) else flags = ACL_START; - bt_cb(skb)->force_active = chan->force_active; + bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); hci_send_acl(hcon, skb, flags); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 405d736131e2..bf196c67ecb1 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -459,7 +459,7 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; } - pwr.force_active = chan->force_active; + pwr.force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); len = min_t(unsigned int, len, sizeof(pwr)); if (copy_to_user(optval, (char *) &pwr, len)) @@ -680,7 +680,11 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch err = -EFAULT; break; } - chan->force_active = pwr.force_active; + + if (pwr.force_active) + set_bit(FLAG_FORCE_ACTIVE, &chan->flags); + else + clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); break; default: @@ -939,7 +943,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->sec_level = pchan->sec_level; chan->role_switch = pchan->role_switch; chan->flags = pchan->flags; - chan->force_active = pchan->force_active; } else { switch (sk->sk_type) { @@ -969,8 +972,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->sec_level = BT_SECURITY_LOW; chan->role_switch = 0; chan->flags = 0; - chan->force_active = BT_POWER_FORCE_ACTIVE_ON; - + set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } /* Default config options */ -- cgit v1.2.3 From 43bd0f32d5cf6593e420b26e2c1c41dc371a47d7 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 14:04:34 +0300 Subject: Bluetooth: convert role_switch variable to flag in l2cap chan role_switch variable inside l2cap_chan is a logical one and can be easily converted to flag Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/l2cap_core.c | 4 ++-- net/bluetooth/l2cap_sock.c | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 440e7b86c01d..aea083c1524a 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -324,7 +324,6 @@ struct l2cap_chan { __le16 sport; __u8 sec_level; - __u8 role_switch; __u8 ident; @@ -463,6 +462,7 @@ enum { /* Definitions for flags in l2cap_chan */ enum { + FLAG_ROLE_SWITCH, FLAG_FORCE_ACTIVE, FLAG_FORCE_RELIABLE, FLAG_FLUSHABLE, diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index aeeacf8076d1..18a08c59f083 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3938,12 +3938,12 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) { lm1 |= HCI_LM_ACCEPT; - if (c->role_switch) + if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm1 |= HCI_LM_MASTER; exact++; } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) { lm2 |= HCI_LM_ACCEPT; - if (c->role_switch) + if (test_bit(FLAG_ROLE_SWITCH, &c->flags)) lm2 |= HCI_LM_MASTER; } } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index bf196c67ecb1..48ad8ba492a5 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -356,7 +356,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us break; } - if (chan->role_switch) + if (test_bit(FLAG_ROLE_SWITCH, &chan->flags)) opt |= L2CAP_LM_MASTER; if (test_bit(FLAG_FORCE_RELIABLE, &chan->flags)) @@ -549,7 +549,10 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us if (opt & L2CAP_LM_SECURE) chan->sec_level = BT_SECURITY_HIGH; - chan->role_switch = (opt & L2CAP_LM_MASTER); + if (opt & L2CAP_LM_MASTER) + set_bit(FLAG_ROLE_SWITCH, &chan->flags); + else + clear_bit(FLAG_ROLE_SWITCH, &chan->flags); if (opt & L2CAP_LM_RELIABLE) set_bit(FLAG_FORCE_RELIABLE, &chan->flags); @@ -941,7 +944,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->max_tx = pchan->max_tx; chan->tx_win = pchan->tx_win; chan->sec_level = pchan->sec_level; - chan->role_switch = pchan->role_switch; chan->flags = pchan->flags; } else { @@ -970,7 +972,6 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->fcs = L2CAP_FCS_CRC16; chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; chan->sec_level = BT_SECURITY_LOW; - chan->role_switch = 0; chan->flags = 0; set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } -- cgit v1.2.3 From 8d6765aa39434ad65a3ae3b695f9c799f32d1d12 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:41 +0300 Subject: Bluetooth: clean up spaces in L2CAP header Spaces converted to tabs Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index aea083c1524a..08ad40bb5a46 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -134,10 +134,9 @@ struct l2cap_conninfo { #define L2CAP_SDU_CONTINUE 0xC000 /* L2CAP Command rej. reasons */ -#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 -#define L2CAP_REJ_MTU_EXCEEDED 0x0001 -#define L2CAP_REJ_INVALID_CID 0x0002 - +#define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 +#define L2CAP_REJ_MTU_EXCEEDED 0x0001 +#define L2CAP_REJ_INVALID_CID 0x0002 /* L2CAP structures */ struct l2cap_hdr { @@ -273,13 +272,13 @@ struct l2cap_info_rsp { } __packed; /* info type */ -#define L2CAP_IT_CL_MTU 0x0001 -#define L2CAP_IT_FEAT_MASK 0x0002 -#define L2CAP_IT_FIXED_CHAN 0x0003 +#define L2CAP_IT_CL_MTU 0x0001 +#define L2CAP_IT_FEAT_MASK 0x0002 +#define L2CAP_IT_FIXED_CHAN 0x0003 /* info result */ -#define L2CAP_IR_SUCCESS 0x0000 -#define L2CAP_IR_NOTSUPP 0x0001 +#define L2CAP_IR_SUCCESS 0x0000 +#define L2CAP_IR_NOTSUPP 0x0001 struct l2cap_conn_param_update_req { __le16 min; -- cgit v1.2.3 From 669bb3962bd7f781879222eeb7263d527551dd5e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 11 Oct 2011 15:57:01 -0300 Subject: Bluetooth: Fix permission of enable_le param With 0444 it is impossible to change the param, changing it to 0644. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d7d96b6b1f0d..0e57634ab8da 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3104,5 +3104,5 @@ void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data) kfree_skb(skb); } -module_param(enable_le, bool, 0444); +module_param(enable_le, bool, 0644); MODULE_PARM_DESC(enable_le, "Enable LE support"); -- cgit v1.2.3 From 6327eb980d2ff0c96363b81cb0ce580165cb81b8 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:42 +0300 Subject: Bluetooth: EWS: extended window size option support Adds support for extended window size (EWS) config option. We enable EWS feature in L2CAP Info RSP when hs enabled. EWS option is included in L2CAP Config Req if tx_win (which is set via socket) bigger then standard default value (63) && hs enabled && remote side supports EWS feature. Using EWS selects extended control field in L2CAP. Code partly based on Qualcomm and Atheros patches sent upstream a year ago. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 8 +++++-- net/bluetooth/l2cap_core.c | 51 ++++++++++++++++++++++++++++++++++++++++--- net/bluetooth/l2cap_sock.c | 8 +++---- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 08ad40bb5a46..51998ff6b8ca 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -32,6 +32,7 @@ #define L2CAP_DEFAULT_MIN_MTU 48 #define L2CAP_DEFAULT_FLUSH_TO 0xffff #define L2CAP_DEFAULT_TX_WINDOW 63 +#define L2CAP_DEFAULT_EXT_WINDOW 0x3FFF #define L2CAP_DEFAULT_MAX_TX 3 #define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ #define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ @@ -233,6 +234,7 @@ struct l2cap_conf_opt { #define L2CAP_CONF_QOS 0x03 #define L2CAP_CONF_RFC 0x04 #define L2CAP_CONF_FCS 0x05 +#define L2CAP_CONF_EWS 0x07 #define L2CAP_CONF_MAX_SIZE 22 @@ -333,7 +335,7 @@ struct l2cap_chan { __u8 fcs; - __u8 tx_win; + __u16 tx_win; __u8 max_tx; __u16 retrans_timeout; __u16 monitor_timeout; @@ -357,7 +359,7 @@ struct l2cap_chan { struct sk_buff *sdu; struct sk_buff *sdu_last_frag; - __u8 remote_tx_win; + __u16 remote_tx_win; __u8 remote_max_tx; __u16 remote_mps; @@ -442,6 +444,7 @@ enum { CONF_CONNECT_PEND, CONF_NO_FCS_RECV, CONF_STATE2_DEVICE, + CONF_EWS_RECV, }; #define L2CAP_CONF_MAX_CONF_REQ 2 @@ -465,6 +468,7 @@ enum { FLAG_FORCE_ACTIVE, FLAG_FORCE_RELIABLE, FLAG_FLUSHABLE, + FLAG_EXT_CTRL, }; #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 18a08c59f083..6e343126f388 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1898,6 +1898,22 @@ static inline __u8 l2cap_select_mode(__u8 mode, __u16 remote_feat_mask) } } +static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) +{ + return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; +} + +static inline void l2cap_txwin_setup(struct l2cap_chan *chan) +{ + if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && + __l2cap_ews_supported(chan)) + /* use extended control field */ + set_bit(FLAG_EXT_CTRL, &chan->flags); + else + chan->tx_win = min_t(u16, chan->tx_win, + L2CAP_DEFAULT_TX_WINDOW); +} + static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) { struct l2cap_conf_req *req = data; @@ -1944,7 +1960,6 @@ done: case L2CAP_MODE_ERTM: rfc.mode = L2CAP_MODE_ERTM; - rfc.txwin_size = chan->tx_win; rfc.max_transmit = chan->max_tx; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; @@ -1952,6 +1967,11 @@ done: if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); + l2cap_txwin_setup(chan); + + rfc.txwin_size = min_t(u16, chan->tx_win, + L2CAP_DEFAULT_TX_WINDOW); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -1963,6 +1983,10 @@ done: chan->fcs = L2CAP_FCS_NONE; l2cap_add_conf_opt(&ptr, L2CAP_CONF_FCS, 1, chan->fcs); } + + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, + chan->tx_win); break; case L2CAP_MODE_STREAMING: @@ -2038,6 +2062,15 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) break; + case L2CAP_CONF_EWS: + if (!enable_hs) + return -ECONNREFUSED; + + set_bit(FLAG_EXT_CTRL, &chan->flags); + set_bit(CONF_EWS_RECV, &chan->conf_state); + chan->remote_tx_win = val; + break; + default: if (hint) break; @@ -2098,7 +2131,11 @@ done: break; case L2CAP_MODE_ERTM: - chan->remote_tx_win = rfc.txwin_size; + if (!test_bit(CONF_EWS_RECV, &chan->conf_state)) + chan->remote_tx_win = rfc.txwin_size; + else + rfc.txwin_size = L2CAP_DEFAULT_TX_WINDOW; + chan->remote_max_tx = rfc.max_transmit; if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) @@ -2190,6 +2227,13 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); break; + + case L2CAP_CONF_EWS: + chan->tx_win = min_t(u16, val, + L2CAP_DEFAULT_EXT_WINDOW); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, + 2, chan->tx_win); + break; } } @@ -2785,7 +2829,8 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm feat_mask |= L2CAP_FEAT_ERTM | L2CAP_FEAT_STREAMING | L2CAP_FEAT_FCS; if (enable_hs) - feat_mask |= L2CAP_FEAT_EXT_FLOW; + feat_mask |= L2CAP_FEAT_EXT_FLOW + | L2CAP_FEAT_EXT_WINDOW; put_unaligned_le32(feat_mask, rsp->data); l2cap_send_cmd(conn, cmd->ident, diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 48ad8ba492a5..836d12e66a38 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -331,7 +331,7 @@ static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __us opts.mode = chan->mode; opts.fcs = chan->fcs; opts.max_tx = chan->max_tx; - opts.txwin_size = (__u16)chan->tx_win; + opts.txwin_size = chan->tx_win; len = min_t(unsigned int, len, sizeof(opts)); if (copy_to_user(optval, (char *) &opts, len)) @@ -501,7 +501,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us opts.mode = chan->mode; opts.fcs = chan->fcs; opts.max_tx = chan->max_tx; - opts.txwin_size = (__u16)chan->tx_win; + opts.txwin_size = chan->tx_win; len = min_t(unsigned int, sizeof(opts), optlen); if (copy_from_user((char *) &opts, optval, len)) { @@ -509,7 +509,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us break; } - if (opts.txwin_size > L2CAP_DEFAULT_TX_WINDOW) { + if (opts.txwin_size > L2CAP_DEFAULT_EXT_WINDOW) { err = -EINVAL; break; } @@ -533,7 +533,7 @@ static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __us chan->omtu = opts.omtu; chan->fcs = opts.fcs; chan->max_tx = opts.max_tx; - chan->tx_win = (__u8)opts.txwin_size; + chan->tx_win = opts.txwin_size; break; case L2CAP_LM: -- cgit v1.2.3 From 57253fd8c91e76780e9628451f680efcbcc52c85 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:43 +0300 Subject: Bluetooth: EWS: adds ext control field bit mask Adds extended control field bit masks and rearrange defines to logical groups: masks, flags and shift groups. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 41 +++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 51998ff6b8ca..fa7edabde4d9 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -109,18 +109,35 @@ struct l2cap_conninfo { #define L2CAP_FCS_CRC16 0x01 /* L2CAP Control Field bit masks */ -#define L2CAP_CTRL_SAR 0xC000 -#define L2CAP_CTRL_REQSEQ 0x3F00 -#define L2CAP_CTRL_TXSEQ 0x007E -#define L2CAP_CTRL_RETRANS 0x0080 -#define L2CAP_CTRL_FINAL 0x0080 -#define L2CAP_CTRL_POLL 0x0010 -#define L2CAP_CTRL_SUPERVISE 0x000C -#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ - -#define L2CAP_CTRL_TXSEQ_SHIFT 1 -#define L2CAP_CTRL_REQSEQ_SHIFT 8 -#define L2CAP_CTRL_SAR_SHIFT 14 +#define L2CAP_CTRL_SAR 0xC000 +#define L2CAP_CTRL_REQSEQ 0x3F00 +#define L2CAP_CTRL_TXSEQ 0x007E +#define L2CAP_CTRL_SUPERVISE 0x000C + +#define L2CAP_CTRL_RETRANS 0x0080 +#define L2CAP_CTRL_FINAL 0x0080 +#define L2CAP_CTRL_POLL 0x0010 +#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */ + +#define L2CAP_CTRL_TXSEQ_SHIFT 1 +#define L2CAP_CTRL_SUPER_SHIFT 2 +#define L2CAP_CTRL_REQSEQ_SHIFT 8 +#define L2CAP_CTRL_SAR_SHIFT 14 + +/* L2CAP Extended Control Field bit mask */ +#define L2CAP_EXT_CTRL_TXSEQ 0xFFFC0000 +#define L2CAP_EXT_CTRL_SAR 0x00030000 +#define L2CAP_EXT_CTRL_SUPERVISE 0x00030000 +#define L2CAP_EXT_CTRL_REQSEQ 0x0000FFFC + +#define L2CAP_EXT_CTRL_POLL 0x00040000 +#define L2CAP_EXT_CTRL_FINAL 0x00000002 +#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */ + +#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2 +#define L2CAP_EXT_CTRL_SAR_SHIFT 16 +#define L2CAP_EXT_CTRL_SUPER_SHIFT 16 +#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18 /* L2CAP Supervisory Function */ #define L2CAP_SUPER_RCV_READY 0x0000 -- cgit v1.2.3 From ab784b7383735681660ccbdda4569fff196c2672 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:44 +0300 Subject: Bluetooth: EWS: rewrite handling Supervisory (S) bits Supervisory bits occupy different windows in standard / extended control fields. Convert hardcoded masks to relative ones and use shift to access S-bit window. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 27 +++++++++++++++++++++++---- net/bluetooth/l2cap_core.c | 41 ++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index fa7edabde4d9..f24f5cf820d7 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -140,10 +140,10 @@ struct l2cap_conninfo { #define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18 /* L2CAP Supervisory Function */ -#define L2CAP_SUPER_RCV_READY 0x0000 -#define L2CAP_SUPER_REJECT 0x0004 -#define L2CAP_SUPER_RCV_NOT_READY 0x0008 -#define L2CAP_SUPER_SELECT_REJECT 0x000C +#define L2CAP_SUPER_RR 0x00 +#define L2CAP_SUPER_REJ 0x01 +#define L2CAP_SUPER_RNR 0x02 +#define L2CAP_SUPER_SREJ 0x03 /* L2CAP Segmentation and Reassembly */ #define L2CAP_SDU_UNSEGMENTED 0x0000 @@ -518,6 +518,25 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch) #define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) #define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START) +static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_SUPERVISE) >> + L2CAP_EXT_CTRL_SUPER_SHIFT; + else + return (ctrl & L2CAP_CTRL_SUPERVISE) >> L2CAP_CTRL_SUPER_SHIFT; +} + +static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (super << L2CAP_EXT_CTRL_SUPER_SHIFT) & + L2CAP_EXT_CTRL_SUPERVISE; + else + return (super << L2CAP_CTRL_SUPER_SHIFT) & + L2CAP_CTRL_SUPERVISE; +} + extern int disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6e343126f388..93b5da69cda5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -613,10 +613,10 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) { if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); set_bit(CONN_RNR_SENT, &chan->conn_state); } else - control |= L2CAP_SUPER_RCV_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; @@ -1408,7 +1408,7 @@ static void l2cap_send_ack(struct l2cap_chan *chan) control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); set_bit(CONN_RNR_SENT, &chan->conn_state); l2cap_send_sframe(chan, control); return; @@ -1417,7 +1417,7 @@ static void l2cap_send_ack(struct l2cap_chan *chan) if (l2cap_ertm_send(chan) > 0) return; - control |= L2CAP_SUPER_RCV_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); } @@ -1426,7 +1426,7 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan) struct srej_list *tail; u16 control; - control = L2CAP_SUPER_SELECT_REJECT; + control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); control |= L2CAP_CTRL_FINAL; tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); @@ -3119,7 +3119,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); l2cap_send_sframe(chan, control); set_bit(CONN_RNR_SENT, &chan->conn_state); } @@ -3131,7 +3131,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) if (!test_bit(CONN_LOCAL_BUSY, &chan->conn_state) && chan->frames_sent == 0) { - control |= L2CAP_SUPER_RCV_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); } } @@ -3287,7 +3287,7 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) set_bit(CONN_LOCAL_BUSY, &chan->conn_state); control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - control |= L2CAP_SUPER_RCV_NOT_READY; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); l2cap_send_sframe(chan, control); set_bit(CONN_RNR_SENT, &chan->conn_state); @@ -3303,7 +3303,8 @@ static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) goto done; control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; - control |= L2CAP_SUPER_RCV_READY | L2CAP_CTRL_POLL; + control |= L2CAP_CTRL_POLL; + control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); chan->retry_count = 1; @@ -3367,7 +3368,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) kfree(l); return; } - control = L2CAP_SUPER_SELECT_REJECT; + control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; l2cap_send_sframe(chan, control); list_del(&l->list); @@ -3381,7 +3382,7 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) u16 control; while (tx_seq != chan->expected_tx_seq) { - control = L2CAP_SUPER_SELECT_REJECT; + control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; l2cap_send_sframe(chan, control); @@ -3645,10 +3646,12 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c return; } - if (rx_control & L2CAP_CTRL_POLL) + if (rx_control & L2CAP_CTRL_POLL) { l2cap_send_srejtail(chan); - else - l2cap_send_sframe(chan, L2CAP_SUPER_RCV_READY); + } else { + rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR); + l2cap_send_sframe(chan, rx_control); + } } static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) @@ -3663,20 +3666,20 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont clear_bit(CONN_WAIT_F, &chan->conn_state); } - switch (rx_control & L2CAP_CTRL_SUPERVISE) { - case L2CAP_SUPER_RCV_READY: + switch (__get_ctrl_super(chan, rx_control)) { + case L2CAP_SUPER_RR: l2cap_data_channel_rrframe(chan, rx_control); break; - case L2CAP_SUPER_REJECT: + case L2CAP_SUPER_REJ: l2cap_data_channel_rejframe(chan, rx_control); break; - case L2CAP_SUPER_SELECT_REJECT: + case L2CAP_SUPER_SREJ: l2cap_data_channel_srejframe(chan, rx_control); break; - case L2CAP_SUPER_RCV_NOT_READY: + case L2CAP_SUPER_RNR: l2cap_data_channel_rnrframe(chan, rx_control); break; } -- cgit v1.2.3 From 7e0ef6ee13ecdf38c2c8b0b0c8ef729710b994eb Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:45 +0300 Subject: Bluetooth: EWS: rewrite handling SAR bits Segmentation and Reassembly (SAR) occupies different windows in standard and extended control fields. Convert hardcoded masks to relative ones and use shift to access SAR bits. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 37 ++++++++++++++++++++++++++++++++----- net/bluetooth/l2cap_core.c | 30 +++++++++++++++--------------- 2 files changed, 47 insertions(+), 20 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index f24f5cf820d7..0759ac68af5e 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -146,10 +146,10 @@ struct l2cap_conninfo { #define L2CAP_SUPER_SREJ 0x03 /* L2CAP Segmentation and Reassembly */ -#define L2CAP_SDU_UNSEGMENTED 0x0000 -#define L2CAP_SDU_START 0x4000 -#define L2CAP_SDU_END 0x8000 -#define L2CAP_SDU_CONTINUE 0xC000 +#define L2CAP_SAR_UNSEGMENTED 0x00 +#define L2CAP_SAR_START 0x01 +#define L2CAP_SAR_END 0x02 +#define L2CAP_SAR_CONTINUE 0x03 /* L2CAP Command rej. reasons */ #define L2CAP_REJ_NOT_UNDERSTOOD 0x0000 @@ -516,7 +516,34 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch) #define __get_reqseq(ctrl) (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8) #define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE)) #define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) -#define __is_sar_start(ctrl) (((ctrl) & L2CAP_CTRL_SAR) == L2CAP_SDU_START) +static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_SAR) >> L2CAP_EXT_CTRL_SAR_SHIFT; + else + return (ctrl & L2CAP_CTRL_SAR) >> L2CAP_CTRL_SAR_SHIFT; +} + +static inline __u32 __set_ctrl_sar(struct l2cap_chan *chan, __u32 sar) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (sar << L2CAP_EXT_CTRL_SAR_SHIFT) & L2CAP_EXT_CTRL_SAR; + else + return (sar << L2CAP_CTRL_SAR_SHIFT) & L2CAP_CTRL_SAR; +} + +static inline bool __is_sar_start(struct l2cap_chan *chan, __u32 ctrl) +{ + return __get_ctrl_sar(chan, ctrl) == L2CAP_SAR_START; +} + +static inline __u32 __get_sar_mask(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_SAR; + else + return L2CAP_CTRL_SAR; +} static inline __u8 __get_ctrl_super(struct l2cap_chan *chan, __u32 ctrl) { diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 93b5da69cda5..9ee42ba808f6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1311,7 +1311,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) tx_skb = skb_clone(skb, GFP_ATOMIC); bt_cb(skb)->retries++; control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); - control &= L2CAP_CTRL_SAR; + control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; @@ -1351,7 +1351,7 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) bt_cb(skb)->retries++; control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); - control &= L2CAP_CTRL_SAR; + control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; @@ -1582,7 +1582,7 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si size_t size = 0; skb_queue_head_init(&sar_queue); - control = L2CAP_SDU_START; + control = __set_ctrl_sar(chan, L2CAP_SAR_START); skb = l2cap_create_iframe_pdu(chan, msg, chan->remote_mps, control, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1595,10 +1595,10 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si size_t buflen; if (len > chan->remote_mps) { - control = L2CAP_SDU_CONTINUE; + control = __set_ctrl_sar(chan, L2CAP_SAR_CONTINUE); buflen = chan->remote_mps; } else { - control = L2CAP_SDU_END; + control = __set_ctrl_sar(chan, L2CAP_SAR_END); buflen = len; } @@ -1654,7 +1654,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) case L2CAP_MODE_STREAMING: /* Entire SDU fits into one PDU */ if (len <= chan->remote_mps) { - control = L2CAP_SDU_UNSEGMENTED; + control = __set_ctrl_sar(chan, L2CAP_SAR_UNSEGMENTED); skb = l2cap_create_iframe_pdu(chan, msg, len, control, 0); if (IS_ERR(skb)) @@ -3201,15 +3201,15 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 { int err = -EINVAL; - switch (control & L2CAP_CTRL_SAR) { - case L2CAP_SDU_UNSEGMENTED: + switch (__get_ctrl_sar(chan, control)) { + case L2CAP_SAR_UNSEGMENTED: if (chan->sdu) break; err = chan->ops->recv(chan->data, skb); break; - case L2CAP_SDU_START: + case L2CAP_SAR_START: if (chan->sdu) break; @@ -3231,7 +3231,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 err = 0; break; - case L2CAP_SDU_CONTINUE: + case L2CAP_SAR_CONTINUE: if (!chan->sdu) break; @@ -3245,7 +3245,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 err = 0; break; - case L2CAP_SDU_END: + case L2CAP_SAR_END: if (!chan->sdu) break; @@ -3343,7 +3343,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) break; skb = skb_dequeue(&chan->srej_q); - control = bt_cb(skb)->sar << L2CAP_CTRL_SAR_SHIFT; + control = __set_ctrl_sar(chan, bt_cb(skb)->sar); err = l2cap_reassemble_sdu(chan, skb, control); if (err < 0) { @@ -3398,7 +3398,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont { u8 tx_seq = __get_txseq(rx_control); u8 req_seq = __get_reqseq(rx_control); - u8 sar = rx_control >> L2CAP_CTRL_SAR_SHIFT; + u8 sar = __get_ctrl_sar(chan, rx_control); int tx_seq_offset, expected_tx_seq_offset; int num_to_ack = (chan->tx_win/6) + 1; int err = 0; @@ -3707,7 +3707,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) if (l2cap_check_fcs(chan, skb)) goto drop; - if (__is_sar_start(control) && __is_iframe(control)) + if (__is_sar_start(chan, control) && __is_iframe(control)) len -= 2; if (chan->fcs == L2CAP_FCS_CRC16) @@ -3811,7 +3811,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (l2cap_check_fcs(chan, skb)) goto drop; - if (__is_sar_start(control)) + if (__is_sar_start(chan, control)) len -= 2; if (chan->fcs == L2CAP_FCS_CRC16) -- cgit v1.2.3 From 0b209fae88c6e844f2ee9d4d791f0f31f7f42ae9 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:46 +0300 Subject: Bluetooth: EWS: rewrite reqseq calculation reqseq calculation uses now information about control field type. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 20 ++++++++++++++++++-- net/bluetooth/l2cap_core.c | 42 +++++++++++++++++++++--------------------- 2 files changed, 39 insertions(+), 23 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 0759ac68af5e..57b64bb51b6b 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -365,11 +365,11 @@ struct l2cap_chan { __u8 next_tx_seq; __u8 expected_ack_seq; __u8 expected_tx_seq; - __u8 buffer_seq; __u8 buffer_seq_srej; __u8 srej_save_reqseq; __u8 frames_sent; __u8 unacked_frames; + __u16 buffer_seq; __u8 retry_count; __u8 num_acked; __u16 sdu_len; @@ -512,8 +512,24 @@ static inline int l2cap_tx_window_full(struct l2cap_chan *ch) return sub == ch->remote_tx_win; } +static inline __u16 __get_reqseq(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_REQSEQ) >> + L2CAP_EXT_CTRL_REQSEQ_SHIFT; + else + return (ctrl & L2CAP_CTRL_REQSEQ) >> L2CAP_CTRL_REQSEQ_SHIFT; +} + +static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (reqseq << L2CAP_EXT_CTRL_REQSEQ_SHIFT) & + L2CAP_EXT_CTRL_REQSEQ; + else + return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ; +} #define __get_txseq(ctrl) (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1) -#define __get_reqseq(ctrl) (((ctrl) & L2CAP_CTRL_REQSEQ) >> 8) #define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE)) #define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9ee42ba808f6..f35eb73b25fd 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -618,7 +618,7 @@ static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) } else control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); - control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->buffer_seq); l2cap_send_sframe(chan, control); } @@ -1316,8 +1316,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) - | (tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + control |= __set_reqseq(chan, chan->buffer_seq); + control |= tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1356,8 +1356,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; - control |= (chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT) - | (chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT); + control |= __set_reqseq(chan, chan->buffer_seq); + control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1405,7 +1405,7 @@ static void l2cap_send_ack(struct l2cap_chan *chan) { u16 control = 0; - control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->buffer_seq); if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); @@ -1430,7 +1430,7 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan) control |= L2CAP_CTRL_FINAL; tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); - control |= tail->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, tail->tx_seq); l2cap_send_sframe(chan, control); } @@ -3116,7 +3116,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) chan->frames_sent = 0; - control |= chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->buffer_seq); if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); @@ -3286,7 +3286,7 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) set_bit(CONN_LOCAL_BUSY, &chan->conn_state); - control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control = __set_reqseq(chan, chan->buffer_seq); control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); l2cap_send_sframe(chan, control); @@ -3302,7 +3302,7 @@ static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; - control = chan->buffer_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control = __set_reqseq(chan, chan->buffer_seq); control |= L2CAP_CTRL_POLL; control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); @@ -3369,7 +3369,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) return; } control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= l->tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, l->tx_seq); l2cap_send_sframe(chan, control); list_del(&l->list); list_add_tail(&l->list, &chan->srej_l); @@ -3383,7 +3383,7 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) while (tx_seq != chan->expected_tx_seq) { control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= chan->expected_tx_seq << L2CAP_CTRL_REQSEQ_SHIFT; + control |= __set_reqseq(chan, chan->expected_tx_seq); l2cap_send_sframe(chan, control); new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); @@ -3397,7 +3397,7 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) { u8 tx_seq = __get_txseq(rx_control); - u8 req_seq = __get_reqseq(rx_control); + u16 req_seq = __get_reqseq(chan, rx_control); u8 sar = __get_ctrl_sar(chan, rx_control); int tx_seq_offset, expected_tx_seq_offset; int num_to_ack = (chan->tx_win/6) + 1; @@ -3531,10 +3531,10 @@ drop: static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control) { - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, __get_reqseq(rx_control), - rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, + __get_reqseq(chan, rx_control), rx_control); - chan->expected_ack_seq = __get_reqseq(rx_control); + chan->expected_ack_seq = __get_reqseq(chan, rx_control); l2cap_drop_acked_frames(chan); if (rx_control & L2CAP_CTRL_POLL) { @@ -3571,7 +3571,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control) { - u8 tx_seq = __get_reqseq(rx_control); + u16 tx_seq = __get_reqseq(chan, rx_control); BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); @@ -3592,7 +3592,7 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c } static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control) { - u8 tx_seq = __get_reqseq(rx_control); + u16 tx_seq = __get_reqseq(chan, rx_control); BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); @@ -3628,7 +3628,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control) { - u8 tx_seq = __get_reqseq(rx_control); + u16 tx_seq = __get_reqseq(chan, rx_control); BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); @@ -3692,7 +3692,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; u16 control; - u8 req_seq; + u16 req_seq; int len, next_tx_seq_offset, req_seq_offset; control = get_unaligned_le16(skb->data); @@ -3718,7 +3718,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } - req_seq = __get_reqseq(control); + req_seq = __get_reqseq(chan, control); req_seq_offset = (req_seq - chan->expected_ack_seq) % 64; if (req_seq_offset < 0) req_seq_offset += 64; -- cgit v1.2.3 From fb45de7dbaf2cf8eec43a88bdb98889f0d4d5d5f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:47 +0300 Subject: Bluetooth: EWS: rewrite L2CAP ERTM txseq calculation L2CAP ERTM txseq calculation uses now information about control field type. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 36 +++++++++++++++++++++++++++--------- net/bluetooth/l2cap_core.c | 22 +++++++++++----------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 57b64bb51b6b..3ca24af7ebc5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -316,7 +316,7 @@ struct l2cap_conn_param_update_rsp { /* ----- L2CAP channels and connections ----- */ struct srej_list { - __u8 tx_seq; + __u16 tx_seq; struct list_head list; }; @@ -362,14 +362,14 @@ struct l2cap_chan { unsigned long conn_state; unsigned long flags; - __u8 next_tx_seq; - __u8 expected_ack_seq; - __u8 expected_tx_seq; - __u8 buffer_seq_srej; - __u8 srej_save_reqseq; - __u8 frames_sent; - __u8 unacked_frames; + __u16 next_tx_seq; + __u16 expected_ack_seq; + __u16 expected_tx_seq; __u16 buffer_seq; + __u16 buffer_seq_srej; + __u16 srej_save_reqseq; + __u16 frames_sent; + __u16 unacked_frames; __u8 retry_count; __u8 num_acked; __u16 sdu_len; @@ -529,7 +529,25 @@ static inline __u32 __set_reqseq(struct l2cap_chan *chan, __u32 reqseq) else return (reqseq << L2CAP_CTRL_REQSEQ_SHIFT) & L2CAP_CTRL_REQSEQ; } -#define __get_txseq(ctrl) (((ctrl) & L2CAP_CTRL_TXSEQ) >> 1) + +static inline __u16 __get_txseq(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (ctrl & L2CAP_EXT_CTRL_TXSEQ) >> + L2CAP_EXT_CTRL_TXSEQ_SHIFT; + else + return (ctrl & L2CAP_CTRL_TXSEQ) >> L2CAP_CTRL_TXSEQ_SHIFT; +} + +static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return (txseq << L2CAP_EXT_CTRL_TXSEQ_SHIFT) & + L2CAP_EXT_CTRL_TXSEQ; + else + return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ; +} + #define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE)) #define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f35eb73b25fd..1c367d6af995 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1270,7 +1270,7 @@ static void l2cap_streaming_send(struct l2cap_chan *chan) while ((skb = skb_dequeue(&chan->tx_q))) { control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE); - control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; + control |= __set_txseq(chan, chan->next_tx_seq); put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { @@ -1284,7 +1284,7 @@ static void l2cap_streaming_send(struct l2cap_chan *chan) } } -static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) { struct sk_buff *skb, *tx_skb; u16 control, fcs; @@ -1317,7 +1317,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u8 tx_seq) control |= L2CAP_CTRL_FINAL; control |= __set_reqseq(chan, chan->buffer_seq); - control |= tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; + control |= __set_txseq(chan, tx_seq); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -1357,7 +1357,7 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) control |= L2CAP_CTRL_FINAL; control |= __set_reqseq(chan, chan->buffer_seq); - control |= chan->next_tx_seq << L2CAP_CTRL_TXSEQ_SHIFT; + control |= __set_txseq(chan, chan->next_tx_seq); put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); @@ -3136,7 +3136,7 @@ static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) } } -static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u8 tx_seq, u8 sar) +static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, u16 tx_seq, u8 sar) { struct sk_buff *next_skb; int tx_seq_offset, next_tx_seq_offset; @@ -3330,7 +3330,7 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy) } } -static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) { struct sk_buff *skb; u16 control; @@ -3357,7 +3357,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u8 tx_seq) } } -static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) { struct srej_list *l, *tmp; u16 control; @@ -3376,7 +3376,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u8 tx_seq) } } -static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) +static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) { struct srej_list *new; u16 control; @@ -3396,7 +3396,7 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u8 tx_seq) static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) { - u8 tx_seq = __get_txseq(rx_control); + u16 tx_seq = __get_txseq(chan, rx_control); u16 req_seq = __get_reqseq(chan, rx_control); u8 sar = __get_ctrl_sar(chan, rx_control); int tx_seq_offset, expected_tx_seq_offset; @@ -3763,7 +3763,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk struct l2cap_chan *chan; struct sock *sk = NULL; u16 control; - u8 tx_seq; + u16 tx_seq; int len; chan = l2cap_get_chan_by_scid(conn, cid); @@ -3820,7 +3820,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (len > chan->mps || len < 0 || __is_sframe(control)) goto drop; - tx_seq = __get_txseq(control); + tx_seq = __get_txseq(chan, control); if (chan->expected_tx_seq != tx_seq) { /* Frame(s) missing - must discard partial SDU */ -- cgit v1.2.3 From 793c2f1cb9d722231290daf1744e6c5b7269f445 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:48 +0300 Subject: Bluetooth: EWS: rewrite check frame type function Check frame function uses now information about control field type. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 18 ++++++++++++++++-- net/bluetooth/l2cap_core.c | 9 +++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3ca24af7ebc5..9444dceaee19 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -548,8 +548,22 @@ static inline __u32 __set_txseq(struct l2cap_chan *chan, __u32 txseq) return (txseq << L2CAP_CTRL_TXSEQ_SHIFT) & L2CAP_CTRL_TXSEQ; } -#define __is_iframe(ctrl) (!((ctrl) & L2CAP_CTRL_FRAME_TYPE)) -#define __is_sframe(ctrl) ((ctrl) & L2CAP_CTRL_FRAME_TYPE) +static inline bool __is_sframe(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_FRAME_TYPE; + else + return ctrl & L2CAP_CTRL_FRAME_TYPE; +} + +static inline __u32 __set_sframe(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_FRAME_TYPE; + else + return L2CAP_CTRL_FRAME_TYPE; +} + static inline __u8 __get_ctrl_sar(struct l2cap_chan *chan, __u32 ctrl) { if (test_bit(FLAG_EXT_CTRL, &chan->flags)) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 1c367d6af995..9262a00bce55 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -578,7 +578,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) BT_DBG("chan %p, control 0x%2.2x", chan, control); count = min_t(unsigned int, conn->mtu, hlen); - control |= L2CAP_CTRL_FRAME_TYPE; + + control |= __set_sframe(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) control |= L2CAP_CTRL_FINAL; @@ -3707,7 +3708,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) if (l2cap_check_fcs(chan, skb)) goto drop; - if (__is_sar_start(chan, control) && __is_iframe(control)) + if (__is_sar_start(chan, control) && !__is_sframe(chan, control)) len -= 2; if (chan->fcs == L2CAP_FCS_CRC16) @@ -3734,7 +3735,7 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) goto drop; } - if (__is_iframe(control)) { + if (!__is_sframe(chan, control)) { if (len < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); goto drop; @@ -3817,7 +3818,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk if (chan->fcs == L2CAP_FCS_CRC16) len -= 2; - if (len > chan->mps || len < 0 || __is_sframe(control)) + if (len > chan->mps || len < 0 || __is_sframe(chan, control)) goto drop; tx_seq = __get_txseq(chan, control); -- cgit v1.2.3 From 03f6715d463e6ee3e724ac64a9bedf1ad7d2b9b4 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:49 +0300 Subject: Bluetooth: EWS: rewrite handling FINAL (F) bit Handle final (F) bit in L2CAP using information about control field type. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 15 +++++++++++++++ net/bluetooth/l2cap_core.c | 20 ++++++++++---------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9444dceaee19..3110c4301e45 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -612,6 +612,21 @@ static inline __u32 __set_ctrl_super(struct l2cap_chan *chan, __u32 super) L2CAP_CTRL_SUPERVISE; } +static inline __u32 __set_ctrl_final(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_FINAL; + else + return L2CAP_CTRL_FINAL; +} + +static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_FINAL; + else + return ctrl & L2CAP_CTRL_FINAL; +} extern int disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 9262a00bce55..c500d1cb39a3 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -582,7 +582,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) control |= __set_sframe(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) control |= L2CAP_CTRL_POLL; @@ -1315,7 +1315,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_txseq(chan, tx_seq); @@ -1355,7 +1355,7 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_txseq(chan, chan->next_tx_seq); @@ -1428,7 +1428,7 @@ static void l2cap_send_srejtail(struct l2cap_chan *chan) u16 control; control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); - control |= L2CAP_CTRL_FINAL; + control |= __set_ctrl_final(chan); tail = list_entry((&chan->srej_l)->prev, struct srej_list, list); control |= __set_reqseq(chan, tail->tx_seq); @@ -3407,7 +3407,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len, tx_seq, rx_control); - if (L2CAP_CTRL_FINAL & rx_control && + if (__is_ctrl_final(chan, rx_control) && test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) @@ -3512,7 +3512,7 @@ expected: return err; } - if (rx_control & L2CAP_CTRL_FINAL) { + if (__is_ctrl_final(chan, rx_control)) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } @@ -3551,7 +3551,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co l2cap_send_i_or_rr_or_rnr(chan); } - } else if (rx_control & L2CAP_CTRL_FINAL) { + } else if (__is_ctrl_final(chan, rx_control)) { clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) @@ -3581,7 +3581,7 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); - if (rx_control & L2CAP_CTRL_FINAL) { + if (__is_ctrl_final(chan, rx_control)) { if (!test_and_clear_bit(CONN_REJ_ACT, &chan->conn_state)) l2cap_retransmit_frames(chan); } else { @@ -3612,7 +3612,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ chan->srej_save_reqseq = tx_seq; set_bit(CONN_SREJ_ACT, &chan->conn_state); } - } else if (rx_control & L2CAP_CTRL_FINAL) { + } else if (__is_ctrl_final(chan, rx_control)) { if (test_bit(CONN_SREJ_ACT, &chan->conn_state) && chan->srej_save_reqseq == tx_seq) clear_bit(CONN_SREJ_ACT, &chan->conn_state); @@ -3659,7 +3659,7 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont { BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len); - if (L2CAP_CTRL_FINAL & rx_control && + if (__is_ctrl_final(chan, rx_control) && test_bit(CONN_WAIT_F, &chan->conn_state)) { __clear_monitor_timer(chan); if (chan->unacked_frames > 0) -- cgit v1.2.3 From e37817353bf94a4e00faad78ffb8cc07f8556252 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:50 +0300 Subject: Bluetooth: EWS: rewrite handling POLL (P) bit Handle POLL (P) bit in L2CAP ERTM using information about control field type. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 16 ++++++++++++++++ net/bluetooth/l2cap_core.c | 14 +++++++------- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3110c4301e45..67a2fdba9d8c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -627,6 +627,22 @@ static inline bool __is_ctrl_final(struct l2cap_chan *chan, __u32 ctrl) else return ctrl & L2CAP_CTRL_FINAL; } + +static inline __u32 __set_ctrl_poll(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_CTRL_POLL; + else + return L2CAP_CTRL_POLL; +} + +static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return ctrl & L2CAP_EXT_CTRL_POLL; + else + return ctrl & L2CAP_CTRL_POLL; +} extern int disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c500d1cb39a3..97aa545d66d9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -585,7 +585,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) control |= __set_ctrl_final(chan); if (test_and_clear_bit(CONN_SEND_PBIT, &chan->conn_state)) - control |= L2CAP_CTRL_POLL; + control |= __set_ctrl_poll(chan); skb = bt_skb_alloc(count, GFP_ATOMIC); if (!skb) @@ -3304,7 +3304,7 @@ static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) goto done; control = __set_reqseq(chan, chan->buffer_seq); - control |= L2CAP_CTRL_POLL; + control |= __set_ctrl_poll(chan); control |= __set_ctrl_super(chan, L2CAP_SUPER_RR); l2cap_send_sframe(chan, control); chan->retry_count = 1; @@ -3538,7 +3538,7 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co chan->expected_ack_seq = __get_reqseq(chan, rx_control); l2cap_drop_acked_frames(chan); - if (rx_control & L2CAP_CTRL_POLL) { + if (__is_ctrl_poll(chan, rx_control)) { set_bit(CONN_SEND_FBIT, &chan->conn_state); if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { if (test_bit(CONN_REMOTE_BUSY, &chan->conn_state) && @@ -3599,7 +3599,7 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); - if (rx_control & L2CAP_CTRL_POLL) { + if (__is_ctrl_poll(chan, rx_control)) { chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); @@ -3637,17 +3637,17 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c chan->expected_ack_seq = tx_seq; l2cap_drop_acked_frames(chan); - if (rx_control & L2CAP_CTRL_POLL) + if (__is_ctrl_poll(chan, rx_control)) set_bit(CONN_SEND_FBIT, &chan->conn_state); if (!test_bit(CONN_SREJ_SENT, &chan->conn_state)) { __clear_retrans_timer(chan); - if (rx_control & L2CAP_CTRL_POLL) + if (__is_ctrl_poll(chan, rx_control)) l2cap_send_rr_or_rnr(chan, L2CAP_CTRL_FINAL); return; } - if (rx_control & L2CAP_CTRL_POLL) { + if (__is_ctrl_poll(chan, rx_control)) { l2cap_send_srejtail(chan); } else { rx_control = __set_ctrl_super(chan, L2CAP_SUPER_RR); -- cgit v1.2.3 From e4ca6d9854dc252e294007fc91249ce34d9a82e8 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:52 +0300 Subject: Bluetooth: EWS: recalculate L2CAP header size Recalculate length of L2CAP header based on Control field length. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 97aa545d66d9..439e7150f150 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -566,12 +566,17 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) struct sk_buff *skb; struct l2cap_hdr *lh; struct l2cap_conn *conn = chan->conn; - int count, hlen = L2CAP_HDR_SIZE + 2; + int count, hlen; u8 flags; if (chan->state != BT_CONNECTED) return; + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + hlen = L2CAP_EXT_HDR_SIZE; + else + hlen = L2CAP_ENH_HDR_SIZE; + if (chan->fcs == L2CAP_FCS_CRC16) hlen += 2; @@ -1534,7 +1539,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; + int err, count, hlen; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, (int)len); @@ -1542,6 +1547,11 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, if (!conn) return ERR_PTR(-ENOTCONN); + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + hlen = L2CAP_EXT_HDR_SIZE; + else + hlen = L2CAP_ENH_HDR_SIZE; + if (sdulen) hlen += 2; @@ -3098,7 +3108,12 @@ static inline void l2cap_sig_channel(struct l2cap_conn *conn, static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) { u16 our_fcs, rcv_fcs; - int hdr_size = L2CAP_HDR_SIZE + 2; + int hdr_size; + + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + hdr_size = L2CAP_EXT_HDR_SIZE; + else + hdr_size = L2CAP_ENH_HDR_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) { skb_trim(skb, skb->len - 2); -- cgit v1.2.3 From d43cb289b065121cbf99434502cf544daf262c5a Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Tue, 11 Oct 2011 13:37:54 +0300 Subject: Bluetooth: EWS: define L2CAP header sizes Adds definitins for L2CAP header sizes to be uses when calculating payload size instead of magic numbers. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 67a2fdba9d8c..806b95093d3f 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -162,6 +162,12 @@ struct l2cap_hdr { __le16 cid; } __packed; #define L2CAP_HDR_SIZE 4 +#define L2CAP_ENH_HDR_SIZE 6 +#define L2CAP_EXT_HDR_SIZE 8 + +#define L2CAP_FCS_SIZE 2 +#define L2CAP_SDULEN_SIZE 2 +#define L2CAP_PSMLEN_SIZE 2 struct l2cap_cmd_hdr { __u8 code; -- cgit v1.2.3 From 5a9e7057c5b3feed2d403ef69cfb0bdbacab3a6d Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 13 Oct 2011 16:18:53 +0300 Subject: Bluetooth: EFS: definitions and headers Define Extended Flow Specification structures and default values. Based upon haijun.liu series of patches (sent Sun, 22 Aug 2010) Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 806b95093d3f..3b54e9b274ef 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -241,6 +241,7 @@ struct l2cap_conf_rsp { #define L2CAP_CONF_UNACCEPT 0x0001 #define L2CAP_CONF_REJECT 0x0002 #define L2CAP_CONF_UNKNOWN 0x0003 +#define L2CAP_CONF_EFS_REJECT 0x0005 struct l2cap_conf_opt { __u8 type; @@ -257,6 +258,7 @@ struct l2cap_conf_opt { #define L2CAP_CONF_QOS 0x03 #define L2CAP_CONF_RFC 0x04 #define L2CAP_CONF_FCS 0x05 +#define L2CAP_CONF_EFS 0x06 #define L2CAP_CONF_EWS 0x07 #define L2CAP_CONF_MAX_SIZE 22 @@ -276,6 +278,15 @@ struct l2cap_conf_rfc { #define L2CAP_MODE_ERTM 0x03 #define L2CAP_MODE_STREAMING 0x04 +struct l2cap_conf_efs { + __u8 id; + __u8 stype; + __le16 msdu; + __le32 sdu_itime; + __le32 acc_lat; + __le32 flush_to; +} __packed; + struct l2cap_disconn_req { __le16 dcid; __le16 scid; @@ -386,6 +397,20 @@ struct l2cap_chan { __u8 remote_max_tx; __u16 remote_mps; + __u8 local_id; + __u8 local_stype; + __u16 local_msdu; + __u32 local_sdu_itime; + __u32 local_acc_lat; + __u32 local_flush_to; + + __u8 remote_id; + __u8 remote_stype; + __u16 remote_msdu; + __u32 remote_sdu_itime; + __u32 remote_acc_lat; + __u32 remote_flush_to; + struct timer_list chan_timer; struct timer_list retrans_timer; struct timer_list monitor_timer; @@ -492,6 +517,7 @@ enum { FLAG_FORCE_RELIABLE, FLAG_FLUSHABLE, FLAG_EXT_CTRL, + FLAG_EFS_ENABLE, }; #define __set_chan_timer(c, t) l2cap_set_timer(c, &c->chan_timer, (t)) -- cgit v1.2.3 From 8f7975b153faab4b78369458a892dd705e7c395b Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 13 Oct 2011 16:18:54 +0300 Subject: Bluetooth: EFS: assign default values in chan add Assign default EFS values when creating L2CAP channel Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 9 +++++++++ net/bluetooth/l2cap_core.c | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 3b54e9b274ef..e67ecd12df8c 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -39,6 +39,9 @@ #define L2CAP_DEFAULT_MAX_PDU_SIZE 1009 /* Sized for 3-DH5 packet */ #define L2CAP_DEFAULT_ACK_TO 200 #define L2CAP_LE_DEFAULT_MTU 23 +#define L2CAP_DEFAULT_MAX_SDU_SIZE 0xFFFF +#define L2CAP_DEFAULT_SDU_ITIME 0xFFFFFFFF +#define L2CAP_DEFAULT_ACC_LAT 0xFFFFFFFF #define L2CAP_CONN_TIMEOUT (40000) /* 40 seconds */ #define L2CAP_INFO_TIMEOUT (4000) /* 4 seconds */ @@ -287,6 +290,12 @@ struct l2cap_conf_efs { __le32 flush_to; } __packed; +#define L2CAP_SERV_NOTRAFIC 0x00 +#define L2CAP_SERV_BESTEFFORT 0x01 +#define L2CAP_SERV_GUARANTEED 0x02 + +#define L2CAP_BESTEFFORT_ID 0x01 + struct l2cap_disconn_req { __le16 dcid; __le16 scid; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 439e7150f150..410c9cda057c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -338,6 +338,13 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) chan->omtu = L2CAP_DEFAULT_MTU; } + chan->local_id = L2CAP_BESTEFFORT_ID; + chan->local_stype = L2CAP_SERV_BESTEFFORT; + chan->local_msdu = L2CAP_DEFAULT_MAX_SDU_SIZE; + chan->local_sdu_itime = L2CAP_DEFAULT_SDU_ITIME; + chan->local_acc_lat = L2CAP_DEFAULT_ACC_LAT; + chan->local_flush_to = L2CAP_DEFAULT_FLUSH_TO; + chan_hold(chan); list_add(&chan->list, &conn->chan_l); -- cgit v1.2.3 From f89cef09cee60a9715150a6e335dce4e64df7400 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 13 Oct 2011 16:18:55 +0300 Subject: Bluetooth: EFS: add efs option in L2CAP conf req Add Extended Flow Specification option when building L2CAP Configuration Request. EFS is added if both the local and remote L2CAP entities have indicated support for the Extended Flow Specification for BR/EDR. ... < ACL data: handle 1 flags 0x00 dlen 10 L2CAP(s): Info req: type 2 > ACL data: handle 1 flags 0x02 dlen 16 L2CAP(s): Info rsp: type 2 result 0 Extended feature mask 0x01f8 Enhanced Retransmission mode Streaming mode FCS Option Extended Flow Specification Fixed Channels Extended Window Size ... < ACL data: handle 1 flags 0x00 dlen 45 L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33 RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498) EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff, AccLat 0xffffffff, FlushTO 0x0000ffff) ... Based upon haijun.liu series of patches (sent Sun, 22 Aug 2010) Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 410c9cda057c..22133464d65b 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1870,6 +1870,37 @@ static void l2cap_add_conf_opt(void **ptr, u8 type, u8 len, unsigned long val) *ptr += L2CAP_CONF_OPT_SIZE + len; } +static void l2cap_add_opt_efs(void **ptr, struct l2cap_chan *chan) +{ + struct l2cap_conf_efs efs; + + switch(chan->mode) { + case L2CAP_MODE_ERTM: + efs.id = chan->local_id; + efs.stype = chan->local_stype; + efs.msdu = cpu_to_le16(chan->local_msdu); + efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); + efs.acc_lat = cpu_to_le32(L2CAP_DEFAULT_ACC_LAT); + efs.flush_to = cpu_to_le32(L2CAP_DEFAULT_FLUSH_TO); + break; + + case L2CAP_MODE_STREAMING: + efs.id = 1; + efs.stype = L2CAP_SERV_BESTEFFORT; + efs.msdu = cpu_to_le16(chan->local_msdu); + efs.sdu_itime = cpu_to_le32(chan->local_sdu_itime); + efs.acc_lat = 0; + efs.flush_to = 0; + break; + + default: + return; + } + + l2cap_add_conf_opt(ptr, L2CAP_CONF_EFS, sizeof(efs), + (unsigned long) &efs); +} + static void l2cap_ack_timeout(unsigned long arg) { struct l2cap_chan *chan = (void *) arg; @@ -1921,6 +1952,11 @@ static inline bool __l2cap_ews_supported(struct l2cap_chan *chan) return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_WINDOW; } +static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) +{ + return enable_hs && chan->conn->feat_mask & L2CAP_FEAT_EXT_FLOW; +} + static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && @@ -1949,6 +1985,9 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) if (test_bit(CONF_STATE2_DEVICE, &chan->conf_state)) break; + if (__l2cap_efs_supported(chan)) + set_bit(FLAG_EFS_ENABLE, &chan->flags); + /* fall through */ default: chan->mode = l2cap_select_mode(rfc.mode, chan->conn->feat_mask); @@ -1993,6 +2032,9 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) + l2cap_add_opt_efs(&ptr, chan); + if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; @@ -2020,6 +2062,9 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) + l2cap_add_opt_efs(&ptr, chan); + if (!(chan->conn->feat_mask & L2CAP_FEAT_FCS)) break; -- cgit v1.2.3 From 928abaa777501ddab94b1b49aae485a2c730d303 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 12 Oct 2011 10:53:57 +0300 Subject: Bluetooth: AMP: read local amp info HCI command Implementation of Read Local AMP Info Command Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 15 +++++++++++++++ include/net/bluetooth/hci_core.h | 11 +++++++++++ net/bluetooth/hci_event.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index aaf79af72432..c5fcd13b9edf 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -726,6 +726,21 @@ struct hci_cp_write_page_scan_activity { #define PAGE_SCAN_TYPE_STANDARD 0x00 #define PAGE_SCAN_TYPE_INTERLACED 0x01 +#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 +struct hci_rp_read_local_amp_info { + __u8 status; + __u8 amp_status; + __le32 total_bw; + __le32 max_bw; + __le32 min_latency; + __le32 max_pdu; + __u8 amp_type; + __le16 pal_cap; + __le16 max_assoc_size; + __le32 max_flush_to; + __le32 be_flush_to; +} __packed; + #define HCI_OP_LE_SET_EVENT_MASK 0x2001 struct hci_cp_le_set_event_mask { __u8 mask[8]; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5b924423cf20..32cddb0f0912 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -150,6 +150,17 @@ struct hci_dev { __u16 sniff_min_interval; __u16 sniff_max_interval; + __u8 amp_status; + __u32 amp_total_bw; + __u32 amp_max_bw; + __u32 amp_min_latency; + __u32 amp_max_pdu; + __u8 amp_type; + __u16 amp_pal_cap; + __u16 amp_assoc_size; + __u32 amp_max_flush_to; + __u32 amp_be_flush_to; + unsigned int auto_accept_delay; unsigned long quirks; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0e57634ab8da..41967fec12b7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -748,6 +748,30 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } +static void hci_cc_read_local_amp_info(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_local_amp_info *rp = (void *) skb->data; + + BT_DBG("%s status 0x%x", hdev->name, rp->status); + + if (rp->status) + return; + + hdev->amp_status = rp->amp_status; + hdev->amp_total_bw = __le32_to_cpu(rp->total_bw); + hdev->amp_max_bw = __le32_to_cpu(rp->max_bw); + hdev->amp_min_latency = __le32_to_cpu(rp->min_latency); + hdev->amp_max_pdu = __le32_to_cpu(rp->max_pdu); + hdev->amp_type = rp->amp_type; + hdev->amp_pal_cap = __le16_to_cpu(rp->pal_cap); + hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size); + hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to); + hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to); + + hci_req_complete(hdev, HCI_OP_READ_LOCAL_AMP_INFO, rp->status); +} + static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, struct sk_buff *skb) { @@ -1898,6 +1922,10 @@ static inline void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *sk hci_cc_write_ca_timeout(hdev, skb); break; + case HCI_OP_READ_LOCAL_AMP_INFO: + hci_cc_read_local_amp_info(hdev, skb); + break; + case HCI_OP_DELETE_STORED_LINK_KEY: hci_cc_delete_stored_link_key(hdev, skb); break; -- cgit v1.2.3 From 0ac7e7002c4d0841197e9ccb8cfecc5b8c58b200 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 8 Oct 2011 14:58:47 +0200 Subject: Bluetooth: Fix hci core device initialization We must not call device_del() if we didn't use device_add(). See module.c for comments on that. Therefore, we need to call device_initialize() when allocating the hci device and later device_add() instead of device_register(). This also fixes a bug when hci_register_dev() failed and we call hci_free_dev() without a valid core device. hci_free_dev() segfaults while calling put_device() on invalid memory. We already do this with hci_conn connections (hci_conn_init_sysfs()) so they do not need to be fixed. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 1 + net/bluetooth/hci_sysfs.c | 18 ++++++++++++------ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 32cddb0f0912..c8cc23c5c936 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -608,6 +608,7 @@ int hci_recv_frame(struct sk_buff *skb); int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); +void hci_init_sysfs(struct hci_dev *hdev); int hci_register_sysfs(struct hci_dev *hdev); void hci_unregister_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b84458dcc226..d2445cb06c42 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -912,6 +912,7 @@ struct hci_dev *hci_alloc_dev(void) if (!hdev) return NULL; + hci_init_sysfs(hdev); skb_queue_head_init(&hdev->driver_init); return hdev; diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 22f1a6c87035..a7d5de3a6b5a 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -542,6 +542,17 @@ static int auto_accept_delay_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, auto_accept_delay_set, "%llu\n"); +void hci_init_sysfs(struct hci_dev *hdev) +{ + struct device *dev = &hdev->dev; + + dev->type = &bt_host; + dev->class = bt_class; + + dev_set_drvdata(dev, hdev); + device_initialize(dev); +} + int hci_register_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; @@ -549,15 +560,10 @@ int hci_register_sysfs(struct hci_dev *hdev) BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); - dev->type = &bt_host; - dev->class = bt_class; dev->parent = hdev->parent; - dev_set_name(dev, "%s", hdev->name); - dev_set_drvdata(dev, hdev); - - err = device_register(dev); + err = device_add(dev); if (err < 0) return err; -- cgit v1.2.3 From ce242970f0934869483221c410d09c00bc8967e7 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 8 Oct 2011 14:58:48 +0200 Subject: Bluetooth: Rename sysfs un/register to add/del As we introduced hci_init_sysfs() we should also rename hci_register_sysfs() and hci_unregister_sysfs() to hci_add_sysfs() and hci_del_sysfs() like we do with hci_conn_add/del_sysfs(). It looks more consistent now. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/hci_sysfs.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c8cc23c5c936..119b795b2850 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -609,8 +609,8 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count); int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count); void hci_init_sysfs(struct hci_dev *hdev); -int hci_register_sysfs(struct hci_dev *hdev); -void hci_unregister_sysfs(struct hci_dev *hdev); +int hci_add_sysfs(struct hci_dev *hdev); +void hci_del_sysfs(struct hci_dev *hdev); void hci_conn_init_sysfs(struct hci_conn *conn); void hci_conn_add_sysfs(struct hci_conn *conn); void hci_conn_del_sysfs(struct hci_conn *conn); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d2445cb06c42..49755785a680 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1506,7 +1506,7 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->workqueue) goto nomem; - hci_register_sysfs(hdev); + hci_add_sysfs(hdev); hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); @@ -1561,7 +1561,7 @@ int hci_unregister_dev(struct hci_dev *hdev) rfkill_destroy(hdev->rfkill); } - hci_unregister_sysfs(hdev); + hci_del_sysfs(hdev); hci_del_off_timer(hdev); del_timer(&hdev->adv_timer); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index a7d5de3a6b5a..1f9f8769e130 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -553,7 +553,7 @@ void hci_init_sysfs(struct hci_dev *hdev) device_initialize(dev); } -int hci_register_sysfs(struct hci_dev *hdev) +int hci_add_sysfs(struct hci_dev *hdev) { struct device *dev = &hdev->dev; int err; @@ -587,7 +587,7 @@ int hci_register_sysfs(struct hci_dev *hdev) return 0; } -void hci_unregister_sysfs(struct hci_dev *hdev) +void hci_del_sysfs(struct hci_dev *hdev) { BT_DBG("%p name %s bus %d", hdev, hdev->name, hdev->bus); -- cgit v1.2.3 From 33ca954daf1ac03c86237b73235d8b0856d84981 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sat, 8 Oct 2011 14:58:49 +0200 Subject: Bluetooth: Forward errors from hci_register_dev We need to catch errors when calling hci_add_sysfs() and return them to the caller to avoid kernel oopses on device_add() failure. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 49755785a680..fdcbf8fc26ad 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1426,7 +1426,7 @@ int hci_add_adv_entry(struct hci_dev *hdev, int hci_register_dev(struct hci_dev *hdev) { struct list_head *head = &hci_dev_list, *p; - int i, id = 0; + int i, id = 0, error; BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name, hdev->bus, hdev->owner); @@ -1503,10 +1503,14 @@ int hci_register_dev(struct hci_dev *hdev) write_unlock_bh(&hci_dev_list_lock); hdev->workqueue = create_singlethread_workqueue(hdev->name); - if (!hdev->workqueue) - goto nomem; + if (!hdev->workqueue) { + error = -ENOMEM; + goto err; + } - hci_add_sysfs(hdev); + error = hci_add_sysfs(hdev); + if (error < 0) + goto err_wqueue; hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, hdev); @@ -1525,12 +1529,14 @@ int hci_register_dev(struct hci_dev *hdev) return id; -nomem: +err_wqueue: + destroy_workqueue(hdev->workqueue); +err: write_lock_bh(&hci_dev_list_lock); list_del(&hdev->list); write_unlock_bh(&hci_dev_list_lock); - return -ENOMEM; + return error; } EXPORT_SYMBOL(hci_register_dev); -- cgit v1.2.3 From 56b7d137855eb02cba8aecbb67d49c24b43644b0 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 14 Oct 2011 19:20:01 -0300 Subject: Bluetooth: return proper error if sock_queue_rcv_skb() fails Improve error handling at cmd_status() and cmd_complete() Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5a94eec06caa..42e26146a9a6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -48,6 +48,7 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_status *ev; + int err; BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); @@ -65,10 +66,11 @@ static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) ev->status = status; put_unaligned_le16(cmd, &ev->opcode); - if (sock_queue_rcv_skb(sk, skb) < 0) + err = sock_queue_rcv_skb(sk, skb); + if (err < 0) kfree_skb(skb); - return 0; + return err; } static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, @@ -77,6 +79,7 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; + int err; BT_DBG("sock %p", sk); @@ -96,10 +99,11 @@ static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, if (rp) memcpy(ev->data, rp, rp_len); - if (sock_queue_rcv_skb(sk, skb) < 0) + err = sock_queue_rcv_skb(sk, skb); + if (err < 0) kfree_skb(skb); - return 0; + return err;; } static int read_version(struct sock *sk) -- cgit v1.2.3 From b7059136d765603f2cff05d5e2d4850a4e505ec8 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 14 Oct 2011 19:23:27 -0300 Subject: Bluetooth: Add missing cmd_status() in mgmt Improve error handling in mgmt load_keys() Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 42e26146a9a6..9d0e22385573 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -917,7 +917,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) cp = (void *) data; if (len < sizeof(*cp)) - return -EINVAL; + return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, EINVAL); key_count = get_unaligned_le16(&cp->key_count); @@ -925,7 +925,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) if (expected_len != len) { BT_ERR("load_keys: expected %u bytes, got %u bytes", len, expected_len); - return -EINVAL; + return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, EINVAL); } hdev = hci_dev_get(index); -- cgit v1.2.3 From 12dc0743015fee37f4090f0937c898294cd2d133 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 14 Oct 2011 19:32:56 -0300 Subject: Bluetooth: Use list_for_each_entry() in mgmt Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9d0e22385573..080cfb6347e8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -694,14 +694,11 @@ static int update_eir(struct hci_dev *hdev) static u8 get_service_classes(struct hci_dev *hdev) { - struct list_head *p; + struct bt_uuid *uuid; u8 val = 0; - list_for_each(p, &hdev->uuids) { - struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); - + list_for_each_entry(uuid, &hdev->uuids, list) val |= uuid->svc_hint; - } return val; } -- cgit v1.2.3 From c636ef58865920c8ba9f877c1040bc73eb61e5cb Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Fri, 14 Oct 2011 19:56:21 -0300 Subject: Bluetooth: Fix mgmt interaction with userspace Partially revert 34918cd7. struct mgmt_key_info needs to have the same size as its version exported to userspace. Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/mgmt.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index d66da0f94f95..3062fd3a65d2 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -101,8 +101,6 @@ struct mgmt_key_info { u8 type; u8 val[16]; u8 pin_len; - u8 dlen; - u8 data[0]; } __packed; #define MGMT_OP_LOAD_KEYS 0x000D -- cgit v1.2.3 From e5b82e58922749e79b84b85cfc6845cbfd1908ed Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 15 Oct 2011 18:03:15 -0300 Subject: Bluetooth: Fix missing cmd_status in mgmt set_service_cache() was missing a cmd_status for the error case. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 080cfb6347e8..9ffd7c3dbb3e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -897,6 +897,9 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, if (err == 0) err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL, 0); + else + cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, -err); + hci_dev_unlock_bh(hdev); hci_dev_put(hdev); -- cgit v1.2.3 From 88843ab06b6f279bff1c32e4218541ac7efe2600 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 17 Oct 2011 12:19:56 +0300 Subject: Bluetooth: EWS: handling different Control fields There are three different Control Field formats: the Standard Control Field, the Enhanced Control Field, and the Extended Control Field. Patch adds function to handle all those fields seamlessly. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 28 +++++++++++++ net/bluetooth/l2cap_core.c | 95 +++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 44 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e67ecd12df8c..41f0906649ee 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -27,6 +27,8 @@ #ifndef __L2CAP_H #define __L2CAP_H +#include + /* L2CAP defaults */ #define L2CAP_DEFAULT_MTU 672 #define L2CAP_DEFAULT_MIN_MTU 48 @@ -684,6 +686,32 @@ static inline bool __is_ctrl_poll(struct l2cap_chan *chan, __u32 ctrl) else return ctrl & L2CAP_CTRL_POLL; } + +static inline __u32 __get_control(struct l2cap_chan *chan, void *p) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return get_unaligned_le32(p); + else + return get_unaligned_le16(p); +} + +static inline void __put_control(struct l2cap_chan *chan, __u32 control, + void *p) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return put_unaligned_le32(control, p); + else + return put_unaligned_le16(control, p); +} + +static inline __u8 __ctrl_size(struct l2cap_chan *chan) +{ + if (test_bit(FLAG_EXT_CTRL, &chan->flags)) + return L2CAP_EXT_HDR_SIZE - L2CAP_HDR_SIZE; + else + return L2CAP_ENH_HDR_SIZE - L2CAP_HDR_SIZE; +} + extern int disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 22133464d65b..a25394231b48 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -568,7 +568,7 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, hci_send_acl(conn->hcon, skb, flags); } -static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) +static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) { struct sk_buff *skb; struct l2cap_hdr *lh; @@ -587,7 +587,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) if (chan->fcs == L2CAP_FCS_CRC16) hlen += 2; - BT_DBG("chan %p, control 0x%2.2x", chan, control); + BT_DBG("chan %p, control 0x%8.8x", chan, control); count = min_t(unsigned int, conn->mtu, hlen); @@ -606,7 +606,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->len = cpu_to_le16(hlen - L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); - put_unaligned_le16(control, skb_put(skb, 2)); + + __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); if (chan->fcs == L2CAP_FCS_CRC16) { u16 fcs = crc16(0, (u8 *)lh, count - 2); @@ -623,7 +624,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u16 control) hci_send_acl(chan->conn->hcon, skb, flags); } -static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u16 control) +static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control) { if (test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { control |= __set_ctrl_super(chan, L2CAP_SUPER_RNR); @@ -1279,12 +1280,13 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) static void l2cap_streaming_send(struct l2cap_chan *chan) { struct sk_buff *skb; - u16 control, fcs; + u32 control; + u16 fcs; while ((skb = skb_dequeue(&chan->tx_q))) { - control = get_unaligned_le16(skb->data + L2CAP_HDR_SIZE); + control = __get_control(chan, skb->data + L2CAP_HDR_SIZE); control |= __set_txseq(chan, chan->next_tx_seq); - put_unaligned_le16(control, skb->data + L2CAP_HDR_SIZE); + __put_control(chan, control, skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)skb->data, skb->len - 2); @@ -1300,7 +1302,8 @@ static void l2cap_streaming_send(struct l2cap_chan *chan) static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) { struct sk_buff *skb, *tx_skb; - u16 control, fcs; + u16 fcs; + u32 control; skb = skb_peek(&chan->tx_q); if (!skb) @@ -1323,7 +1326,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) tx_skb = skb_clone(skb, GFP_ATOMIC); bt_cb(skb)->retries++; - control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + + control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) @@ -1332,7 +1336,7 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_txseq(chan, tx_seq); - put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); @@ -1345,7 +1349,8 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) static int l2cap_ertm_send(struct l2cap_chan *chan) { struct sk_buff *skb, *tx_skb; - u16 control, fcs; + u16 fcs; + u32 control; int nsent = 0; if (chan->state != BT_CONNECTED) @@ -1363,7 +1368,7 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) bt_cb(skb)->retries++; - control = get_unaligned_le16(tx_skb->data + L2CAP_HDR_SIZE); + control = __get_control(chan, tx_skb->data + L2CAP_HDR_SIZE); control &= __get_sar_mask(chan); if (test_and_clear_bit(CONN_SEND_FBIT, &chan->conn_state)) @@ -1371,8 +1376,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) control |= __set_reqseq(chan, chan->buffer_seq); control |= __set_txseq(chan, chan->next_tx_seq); - put_unaligned_le16(control, tx_skb->data + L2CAP_HDR_SIZE); + __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); @@ -1416,7 +1421,7 @@ static int l2cap_retransmit_frames(struct l2cap_chan *chan) static void l2cap_send_ack(struct l2cap_chan *chan) { - u16 control = 0; + u32 control = 0; control |= __set_reqseq(chan, chan->buffer_seq); @@ -1437,7 +1442,7 @@ static void l2cap_send_ack(struct l2cap_chan *chan) static void l2cap_send_srejtail(struct l2cap_chan *chan) { struct srej_list *tail; - u16 control; + u32 control; control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); control |= __set_ctrl_final(chan); @@ -1541,7 +1546,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u16 control, u16 sdulen) + u32 control, u16 sdulen) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1575,7 +1580,9 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); lh->len = cpu_to_le16(len + (hlen - L2CAP_HDR_SIZE)); - put_unaligned_le16(control, skb_put(skb, 2)); + + __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); + if (sdulen) put_unaligned_le16(sdulen, skb_put(skb, 2)); @@ -1596,7 +1603,7 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si { struct sk_buff *skb; struct sk_buff_head sar_queue; - u16 control; + u32 control; size_t size = 0; skb_queue_head_init(&sar_queue); @@ -1640,7 +1647,7 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; - u16 control; + u32 control; int err; /* Connectionless channel */ @@ -3180,7 +3187,7 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) static inline void l2cap_send_i_or_rr_or_rnr(struct l2cap_chan *chan) { - u16 control = 0; + u32 control = 0; chan->frames_sent = 0; @@ -3265,7 +3272,7 @@ static void append_skb_frag(struct sk_buff *skb, skb->truesize += new_frag->truesize; } -static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u16 control) +static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u32 control) { int err = -EINVAL; @@ -3348,7 +3355,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u1 static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) { - u16 control; + u32 control; BT_DBG("chan %p, Enter local busy", chan); @@ -3365,7 +3372,7 @@ static void l2cap_ertm_enter_local_busy(struct l2cap_chan *chan) static void l2cap_ertm_exit_local_busy(struct l2cap_chan *chan) { - u16 control; + u32 control; if (!test_bit(CONN_RNR_SENT, &chan->conn_state)) goto done; @@ -3401,7 +3408,7 @@ void l2cap_chan_busy(struct l2cap_chan *chan, int busy) static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) { struct sk_buff *skb; - u16 control; + u32 control; while ((skb = skb_peek(&chan->srej_q)) && !test_bit(CONN_LOCAL_BUSY, &chan->conn_state)) { @@ -3428,7 +3435,7 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) { struct srej_list *l, *tmp; - u16 control; + u32 control; list_for_each_entry_safe(l, tmp, &chan->srej_l, list) { if (l->tx_seq == tx_seq) { @@ -3447,7 +3454,7 @@ static void l2cap_resend_srejframe(struct l2cap_chan *chan, u16 tx_seq) static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) { struct srej_list *new; - u16 control; + u32 control; while (tx_seq != chan->expected_tx_seq) { control = __set_ctrl_super(chan, L2CAP_SUPER_SREJ); @@ -3462,7 +3469,7 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; } -static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) +static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) { u16 tx_seq = __get_txseq(chan, rx_control); u16 req_seq = __get_reqseq(chan, rx_control); @@ -3471,7 +3478,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u16 rx_cont int num_to_ack = (chan->tx_win/6) + 1; int err = 0; - BT_DBG("chan %p len %d tx_seq %d rx_control 0x%4.4x", chan, skb->len, + BT_DBG("chan %p len %d tx_seq %d rx_control 0x%8.8x", chan, skb->len, tx_seq, rx_control); if (__is_ctrl_final(chan, rx_control) && @@ -3597,9 +3604,9 @@ drop: return 0; } -static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u32 rx_control) { - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, __get_reqseq(chan, rx_control), rx_control); chan->expected_ack_seq = __get_reqseq(chan, rx_control); @@ -3637,11 +3644,11 @@ static inline void l2cap_data_channel_rrframe(struct l2cap_chan *chan, u16 rx_co } } -static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u32 rx_control) { u16 tx_seq = __get_reqseq(chan, rx_control); - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); @@ -3658,11 +3665,11 @@ static inline void l2cap_data_channel_rejframe(struct l2cap_chan *chan, u16 rx_c set_bit(CONN_REJ_ACT, &chan->conn_state); } } -static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u32 rx_control) { u16 tx_seq = __get_reqseq(chan, rx_control); - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); clear_bit(CONN_REMOTE_BUSY, &chan->conn_state); @@ -3694,11 +3701,11 @@ static inline void l2cap_data_channel_srejframe(struct l2cap_chan *chan, u16 rx_ } } -static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_control) +static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u32 rx_control) { u16 tx_seq = __get_reqseq(chan, rx_control); - BT_DBG("chan %p, req_seq %d ctrl 0x%4.4x", chan, tx_seq, rx_control); + BT_DBG("chan %p, req_seq %d ctrl 0x%8.8x", chan, tx_seq, rx_control); set_bit(CONN_REMOTE_BUSY, &chan->conn_state); chan->expected_ack_seq = tx_seq; @@ -3722,9 +3729,9 @@ static inline void l2cap_data_channel_rnrframe(struct l2cap_chan *chan, u16 rx_c } } -static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_control, struct sk_buff *skb) +static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) { - BT_DBG("chan %p rx_control 0x%4.4x len %d", chan, rx_control, skb->len); + BT_DBG("chan %p rx_control 0x%8.8x len %d", chan, rx_control, skb->len); if (__is_ctrl_final(chan, rx_control) && test_bit(CONN_WAIT_F, &chan->conn_state)) { @@ -3759,12 +3766,12 @@ static inline int l2cap_data_channel_sframe(struct l2cap_chan *chan, u16 rx_cont static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) { struct l2cap_chan *chan = l2cap_pi(sk)->chan; - u16 control; + u32 control; u16 req_seq; int len, next_tx_seq_offset, req_seq_offset; - control = get_unaligned_le16(skb->data); - skb_pull(skb, 2); + control = __get_control(chan, skb->data); + skb_pull(skb, __ctrl_size(chan)); len = skb->len; /* @@ -3830,7 +3837,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk { struct l2cap_chan *chan; struct sock *sk = NULL; - u16 control; + u32 control; u16 tx_seq; int len; @@ -3872,8 +3879,8 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto done; case L2CAP_MODE_STREAMING: - control = get_unaligned_le16(skb->data); - skb_pull(skb, 2); + control = __get_control(chan, skb->data); + skb_pull(skb, __ctrl_size(chan)); len = skb->len; if (l2cap_check_fcs(chan, skb)) -- cgit v1.2.3 From 836be934218eb80abc5515d584c329c26951086f Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 17 Oct 2011 12:19:57 +0300 Subject: Bluetooth: EWS: support extended seq numbers Adds support for extended sequence numbers found in extended control fields. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/bluetooth.h | 2 +- include/net/bluetooth/l2cap.h | 17 +++++++++++ net/bluetooth/l2cap_core.c | 63 +++++++++++++++++++-------------------- 3 files changed, 48 insertions(+), 34 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index e727555d4ee9..fb1acb3454ae 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -158,7 +158,7 @@ struct bt_skb_cb { __u8 pkt_type; __u8 incoming; __u16 expect; - __u8 tx_seq; + __u16 tx_seq; __u8 retries; __u8 sar; unsigned short channel; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 41f0906649ee..fddc82afeafc 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -381,6 +381,7 @@ struct l2cap_chan { __u8 fcs; __u16 tx_win; + __u16 tx_win_max; __u8 max_tx; __u16 retrans_timeout; __u16 monitor_timeout; @@ -543,6 +544,22 @@ enum { L2CAP_DEFAULT_ACK_TO); #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) +{ + int offset; + + offset = (seq1 - seq2) % (chan->tx_win_max + 1); + if (offset < 0) + offset += (chan->tx_win_max + 1); + + return offset; +} + +static inline __u16 __next_seq(struct l2cap_chan *chan, __u16 seq) +{ + return (seq + 1) % (chan->tx_win_max + 1); +} + static inline int l2cap_tx_window_full(struct l2cap_chan *ch) { int sub; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a25394231b48..86c8720043ea 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1295,7 +1295,7 @@ static void l2cap_streaming_send(struct l2cap_chan *chan) l2cap_do_send(chan, skb); - chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; + chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); } } @@ -1389,7 +1389,8 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) __set_retrans_timer(chan); bt_cb(skb)->tx_seq = chan->next_tx_seq; - chan->next_tx_seq = (chan->next_tx_seq + 1) % 64; + + chan->next_tx_seq = __next_seq(chan, chan->next_tx_seq); if (bt_cb(skb)->retries == 1) chan->unacked_frames++; @@ -1967,12 +1968,15 @@ static inline bool __l2cap_efs_supported(struct l2cap_chan *chan) static inline void l2cap_txwin_setup(struct l2cap_chan *chan) { if (chan->tx_win > L2CAP_DEFAULT_TX_WINDOW && - __l2cap_ews_supported(chan)) + __l2cap_ews_supported(chan)) { /* use extended control field */ set_bit(FLAG_EXT_CTRL, &chan->flags); - else + chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; + } else { chan->tx_win = min_t(u16, chan->tx_win, L2CAP_DEFAULT_TX_WINDOW); + chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; + } } static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) @@ -2138,6 +2142,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) set_bit(FLAG_EXT_CTRL, &chan->flags); set_bit(CONF_EWS_RECV, &chan->conf_state); + chan->tx_win_max = L2CAP_DEFAULT_EXT_WINDOW; chan->remote_tx_win = val; break; @@ -3225,18 +3230,14 @@ static int l2cap_add_to_srej_queue(struct l2cap_chan *chan, struct sk_buff *skb, return 0; } - tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; - if (tx_seq_offset < 0) - tx_seq_offset += 64; + tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); do { if (bt_cb(next_skb)->tx_seq == tx_seq) return -EINVAL; - next_tx_seq_offset = (bt_cb(next_skb)->tx_seq - - chan->buffer_seq) % 64; - if (next_tx_seq_offset < 0) - next_tx_seq_offset += 64; + next_tx_seq_offset = __seq_offset(chan, + bt_cb(next_skb)->tx_seq, chan->buffer_seq); if (next_tx_seq_offset > tx_seq_offset) { __skb_queue_before(&chan->srej_q, next_skb, skb); @@ -3426,9 +3427,8 @@ static void l2cap_check_srej_gap(struct l2cap_chan *chan, u16 tx_seq) break; } - chan->buffer_seq_srej = - (chan->buffer_seq_srej + 1) % 64; - tx_seq = (tx_seq + 1) % 64; + chan->buffer_seq_srej = __next_seq(chan, chan->buffer_seq_srej); + tx_seq = __next_seq(chan, tx_seq); } } @@ -3463,10 +3463,13 @@ static void l2cap_send_srejframe(struct l2cap_chan *chan, u16 tx_seq) new = kzalloc(sizeof(struct srej_list), GFP_ATOMIC); new->tx_seq = chan->expected_tx_seq; - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + + chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); + list_add_tail(&new->list, &chan->srej_l); } - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + + chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); } static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_control, struct sk_buff *skb) @@ -3492,9 +3495,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont chan->expected_ack_seq = req_seq; l2cap_drop_acked_frames(chan); - tx_seq_offset = (tx_seq - chan->buffer_seq) % 64; - if (tx_seq_offset < 0) - tx_seq_offset += 64; + tx_seq_offset = __seq_offset(chan, tx_seq, chan->buffer_seq); /* invalid tx_seq */ if (tx_seq_offset >= chan->tx_win) { @@ -3542,10 +3543,8 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont l2cap_send_srejframe(chan, tx_seq); } } else { - expected_tx_seq_offset = - (chan->expected_tx_seq - chan->buffer_seq) % 64; - if (expected_tx_seq_offset < 0) - expected_tx_seq_offset += 64; + expected_tx_seq_offset = __seq_offset(chan, + chan->expected_tx_seq, chan->buffer_seq); /* duplicated tx_seq */ if (tx_seq_offset < expected_tx_seq_offset) @@ -3570,7 +3569,7 @@ static inline int l2cap_data_channel_iframe(struct l2cap_chan *chan, u32 rx_cont return 0; expected: - chan->expected_tx_seq = (chan->expected_tx_seq + 1) % 64; + chan->expected_tx_seq = __next_seq(chan, chan->expected_tx_seq); if (test_bit(CONN_SREJ_SENT, &chan->conn_state)) { bt_cb(skb)->tx_seq = tx_seq; @@ -3580,7 +3579,8 @@ expected: } err = l2cap_reassemble_sdu(chan, skb, rx_control); - chan->buffer_seq = (chan->buffer_seq + 1) % 64; + chan->buffer_seq = __next_seq(chan, chan->buffer_seq); + if (err < 0) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); return err; @@ -3794,14 +3794,11 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) } req_seq = __get_reqseq(chan, control); - req_seq_offset = (req_seq - chan->expected_ack_seq) % 64; - if (req_seq_offset < 0) - req_seq_offset += 64; - next_tx_seq_offset = - (chan->next_tx_seq - chan->expected_ack_seq) % 64; - if (next_tx_seq_offset < 0) - next_tx_seq_offset += 64; + req_seq_offset = __seq_offset(chan, req_seq, chan->expected_ack_seq); + + next_tx_seq_offset = __seq_offset(chan, chan->next_tx_seq, + chan->expected_ack_seq); /* check for invalid req-seq */ if (req_seq_offset > next_tx_seq_offset) { @@ -3907,7 +3904,7 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk /* TODO: Notify userland of missing data */ } - chan->expected_tx_seq = (tx_seq + 1) % 64; + chan->expected_tx_seq = __next_seq(chan, tx_seq); if (l2cap_reassemble_sdu(chan, skb, control) == -EMSGSIZE) l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); -- cgit v1.2.3 From 03a512137da58e18bec15b46c409a62e0250447e Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 17 Oct 2011 12:19:58 +0300 Subject: Bluetooth: EWS: remove magic numbers in l2cap Remove magic numbers for FCS, SDU LEN and PSM LEN when calculating packet payload. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 46 ++++++++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 86c8720043ea..aa33499798a6 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -585,7 +585,7 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) hlen = L2CAP_ENH_HDR_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - hlen += 2; + hlen += L2CAP_FCS_SIZE; BT_DBG("chan %p, control 0x%8.8x", chan, control); @@ -610,8 +610,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); if (chan->fcs == L2CAP_FCS_CRC16) { - u16 fcs = crc16(0, (u8 *)lh, count - 2); - put_unaligned_le16(fcs, skb_put(skb, 2)); + u16 fcs = crc16(0, (u8 *)lh, count - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); } if (lmp_no_flush_capable(conn->hcon->hdev)) @@ -1289,8 +1289,10 @@ static void l2cap_streaming_send(struct l2cap_chan *chan) __put_control(chan, control, skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, skb->len - 2); - put_unaligned_le16(fcs, skb->data + skb->len - 2); + fcs = crc16(0, (u8 *)skb->data, + skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, + skb->data + skb->len - L2CAP_FCS_SIZE); } l2cap_do_send(chan, skb); @@ -1339,8 +1341,10 @@ static void l2cap_retransmit_one_frame(struct l2cap_chan *chan, u16 tx_seq) __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)tx_skb->data, tx_skb->len - 2); - put_unaligned_le16(fcs, tx_skb->data + tx_skb->len - 2); + fcs = crc16(0, (u8 *)tx_skb->data, + tx_skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, + tx_skb->data + tx_skb->len - L2CAP_FCS_SIZE); } l2cap_do_send(chan, tx_skb); @@ -1380,8 +1384,10 @@ static int l2cap_ertm_send(struct l2cap_chan *chan) __put_control(chan, control, tx_skb->data + L2CAP_HDR_SIZE); if (chan->fcs == L2CAP_FCS_CRC16) { - fcs = crc16(0, (u8 *)skb->data, tx_skb->len - 2); - put_unaligned_le16(fcs, skb->data + tx_skb->len - 2); + fcs = crc16(0, (u8 *)skb->data, + tx_skb->len - L2CAP_FCS_SIZE); + put_unaligned_le16(fcs, skb->data + + tx_skb->len - L2CAP_FCS_SIZE); } l2cap_do_send(chan, tx_skb); @@ -1491,7 +1497,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; - int err, count, hlen = L2CAP_HDR_SIZE + 2; + int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; BT_DBG("sk %p len %d", sk, (int)len); @@ -1566,10 +1572,10 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, hlen = L2CAP_ENH_HDR_SIZE; if (sdulen) - hlen += 2; + hlen += L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - hlen += 2; + hlen += L2CAP_FCS_SIZE; count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, count + hlen, @@ -1585,7 +1591,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, __put_control(chan, control, skb_put(skb, __ctrl_size(chan))); if (sdulen) - put_unaligned_le16(sdulen, skb_put(skb, 2)); + put_unaligned_le16(sdulen, skb_put(skb, L2CAP_SDULEN_SIZE)); err = l2cap_skbuff_fromiovec(sk, msg, len, count, skb); if (unlikely(err < 0)) { @@ -1594,7 +1600,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, } if (chan->fcs == L2CAP_FCS_CRC16) - put_unaligned_le16(0, skb_put(skb, 2)); + put_unaligned_le16(0, skb_put(skb, L2CAP_FCS_SIZE)); bt_cb(skb)->retries = 0; return skb; @@ -3180,7 +3186,7 @@ static int l2cap_check_fcs(struct l2cap_chan *chan, struct sk_buff *skb) hdr_size = L2CAP_ENH_HDR_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) { - skb_trim(skb, skb->len - 2); + skb_trim(skb, skb->len - L2CAP_FCS_SIZE); rcv_fcs = get_unaligned_le16(skb->data + skb->len); our_fcs = crc16(0, skb->data - hdr_size, skb->len + hdr_size); @@ -3290,7 +3296,7 @@ static int l2cap_reassemble_sdu(struct l2cap_chan *chan, struct sk_buff *skb, u3 break; chan->sdu_len = get_unaligned_le16(skb->data); - skb_pull(skb, 2); + skb_pull(skb, L2CAP_SDULEN_SIZE); if (chan->sdu_len > chan->imtu) { err = -EMSGSIZE; @@ -3783,10 +3789,10 @@ static int l2cap_ertm_data_rcv(struct sock *sk, struct sk_buff *skb) goto drop; if (__is_sar_start(chan, control) && !__is_sframe(chan, control)) - len -= 2; + len -= L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - len -= 2; + len -= L2CAP_FCS_SIZE; if (len > chan->mps) { l2cap_send_disconn_req(chan->conn, chan, ECONNRESET); @@ -3884,10 +3890,10 @@ static inline int l2cap_data_channel(struct l2cap_conn *conn, u16 cid, struct sk goto drop; if (__is_sar_start(chan, control)) - len -= 2; + len -= L2CAP_SDULEN_SIZE; if (chan->fcs == L2CAP_FCS_CRC16) - len -= 2; + len -= L2CAP_FCS_SIZE; if (len > chan->mps || len < 0 || __is_sframe(chan, control)) goto drop; -- cgit v1.2.3 From c8f791626a8840fe60a05ab55468dfb3922cb35a Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 17 Oct 2011 12:19:59 +0300 Subject: Bluetooth: EWS: fix max_pdu calculation Fix max_pdu_size calculationin for RFC. Change magic number to human readable defines. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 42 ++++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 14 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index aa33499798a6..78911269d901 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1990,6 +1990,7 @@ static int l2cap_build_conf_req(struct l2cap_chan *chan, void *data) struct l2cap_conf_req *req = data; struct l2cap_conf_rfc rfc = { .mode = chan->mode }; void *ptr = req->data; + u16 size; BT_DBG("chan %p", chan); @@ -2037,9 +2038,12 @@ done: rfc.max_transmit = chan->max_tx; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); + + size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); l2cap_txwin_setup(chan); @@ -2072,9 +2076,12 @@ done: rfc.max_transmit = 0; rfc.retrans_timeout = 0; rfc.monitor_timeout = 0; - rfc.max_pdu_size = cpu_to_le16(L2CAP_DEFAULT_MAX_PDU_SIZE); - if (L2CAP_DEFAULT_MAX_PDU_SIZE > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); + + size = min_t(u16, L2CAP_DEFAULT_MAX_PDU_SIZE, chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); @@ -2110,6 +2117,7 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; + u16 size; BT_DBG("chan %p", chan); @@ -2219,10 +2227,13 @@ done: chan->remote_max_tx = rfc.max_transmit; - if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); - - chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); + size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), + chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); + chan->remote_mps = size; rfc.retrans_timeout = le16_to_cpu(L2CAP_DEFAULT_RETRANS_TO); @@ -2237,10 +2248,13 @@ done: break; case L2CAP_MODE_STREAMING: - if (le16_to_cpu(rfc.max_pdu_size) > chan->conn->mtu - 10) - rfc.max_pdu_size = cpu_to_le16(chan->conn->mtu - 10); - - chan->remote_mps = le16_to_cpu(rfc.max_pdu_size); + size = min_t(u16, le16_to_cpu(rfc.max_pdu_size), + chan->conn->mtu - + L2CAP_EXT_HDR_SIZE - + L2CAP_SDULEN_SIZE - + L2CAP_FCS_SIZE); + rfc.max_pdu_size = cpu_to_le16(size); + chan->remote_mps = size; set_bit(CONF_MODE_DONE, &chan->conf_state); -- cgit v1.2.3 From 42dceae2819b5ac6fc9a0d414ae05a8960e2a1d9 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 17 Oct 2011 14:35:30 +0300 Subject: Bluetooth: EFS: parse L2CAP config request Add parsing Extended Flow Specification option in L2CAP Config Request Based upon haijun.liu series of patches (sent Sun, 22 Aug 2010) Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 78911269d901..bda6da797734 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2115,6 +2115,8 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) int type, hint, olen; unsigned long val; struct l2cap_conf_rfc rfc = { .mode = L2CAP_MODE_BASIC }; + struct l2cap_conf_efs efs; + u8 remote_efs = 0; u16 mtu = L2CAP_DEFAULT_MTU; u16 result = L2CAP_CONF_SUCCESS; u16 size; @@ -2147,7 +2149,12 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) case L2CAP_CONF_FCS: if (val == L2CAP_FCS_NONE) set_bit(CONF_NO_FCS_RECV, &chan->conf_state); + break; + case L2CAP_CONF_EFS: + remote_efs = 1; + if (olen == sizeof(efs)) + memcpy(&efs, (void *) val, olen); break; case L2CAP_CONF_EWS: @@ -2182,6 +2189,13 @@ static int l2cap_parse_conf_req(struct l2cap_chan *chan, void *data) break; } + if (remote_efs) { + if (__l2cap_efs_supported(chan)) + set_bit(FLAG_EFS_ENABLE, &chan->flags); + else + return -ECONNREFUSED; + } + if (chan->mode != rfc.mode) return -ECONNREFUSED; @@ -2200,7 +2214,6 @@ done: sizeof(rfc), (unsigned long) &rfc); } - if (result == L2CAP_CONF_SUCCESS) { /* Configure output options and let the other side know * which ones we don't like. */ @@ -2213,6 +2226,22 @@ done: } l2cap_add_conf_opt(&ptr, L2CAP_CONF_MTU, 2, chan->omtu); + if (remote_efs) { + if (chan->local_stype != L2CAP_SERV_NOTRAFIC && + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) { + + result = L2CAP_CONF_UNACCEPT; + + if (chan->num_conf_req >= 1) + return -ECONNREFUSED; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, + sizeof(efs), + (unsigned long) &efs); + } + } + switch (rfc.mode) { case L2CAP_MODE_BASIC: chan->fcs = L2CAP_FCS_NONE; @@ -2245,6 +2274,19 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), (unsigned long) &rfc); + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { + chan->remote_id = efs.id; + chan->remote_stype = efs.stype; + chan->remote_msdu = le16_to_cpu(efs.msdu); + chan->remote_flush_to = + le32_to_cpu(efs.flush_to); + chan->remote_acc_lat = + le32_to_cpu(efs.acc_lat); + chan->remote_sdu_itime = + le32_to_cpu(efs.sdu_itime); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, + sizeof(efs), (unsigned long) &efs); + } break; case L2CAP_MODE_STREAMING: -- cgit v1.2.3 From 5ada9913630d48438f2e07551af43cbf297372d4 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 24 Oct 2011 15:30:57 +0200 Subject: Bluetooth: Return proper error codes on rfcomm tty init Forward error codes from tty core to the rfcomm_init caller instead of using generic -1 errors. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/rfcomm/tty.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index c258796313e0..2b753a3f4d61 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1155,9 +1155,11 @@ static const struct tty_operations rfcomm_ops = { int __init rfcomm_init_ttys(void) { + int error; + rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); if (!rfcomm_tty_driver) - return -1; + return -ENOMEM; rfcomm_tty_driver->owner = THIS_MODULE; rfcomm_tty_driver->driver_name = "rfcomm"; @@ -1172,10 +1174,11 @@ int __init rfcomm_init_ttys(void) rfcomm_tty_driver->init_termios.c_lflag &= ~ICANON; tty_set_operations(rfcomm_tty_driver, &rfcomm_ops); - if (tty_register_driver(rfcomm_tty_driver)) { + error = tty_register_driver(rfcomm_tty_driver); + if (error) { BT_ERR("Can't register RFCOMM TTY driver"); put_tty_driver(rfcomm_tty_driver); - return -1; + return error; } BT_INFO("RFCOMM TTY layer initialized"); -- cgit v1.2.3 From 96af7391b752cf3d2de3aef8f03c45ba76d3ac5e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 24 Oct 2011 15:30:58 +0200 Subject: Bluetooth: Replace rfcomm tty tasklet by workqueue Remove old tasklets and replace by workqueue. To avoid reentrancy (which tasklets always avoid) we use the system_nrt_wq. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/rfcomm/tty.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 2b753a3f4d61..947f1b3afd15 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -65,7 +66,7 @@ struct rfcomm_dev { struct rfcomm_dlc *dlc; struct tty_struct *tty; wait_queue_head_t wait; - struct tasklet_struct wakeup_task; + struct work_struct wakeup_task; struct device *tty_dev; @@ -81,7 +82,7 @@ static void rfcomm_dev_data_ready(struct rfcomm_dlc *dlc, struct sk_buff *skb); static void rfcomm_dev_state_change(struct rfcomm_dlc *dlc, int err); static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig); -static void rfcomm_tty_wakeup(unsigned long arg); +static void rfcomm_tty_wakeup(struct work_struct *work); /* ---- Device functions ---- */ static void rfcomm_dev_destruct(struct rfcomm_dev *dev) @@ -257,7 +258,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) atomic_set(&dev->opened, 0); init_waitqueue_head(&dev->wait); - tasklet_init(&dev->wakeup_task, rfcomm_tty_wakeup, (unsigned long) dev); + INIT_WORK(&dev->wakeup_task, rfcomm_tty_wakeup); skb_queue_head_init(&dev->pending); @@ -351,7 +352,7 @@ static void rfcomm_wfree(struct sk_buff *skb) struct rfcomm_dev *dev = (void *) skb->sk; atomic_sub(skb->truesize, &dev->wmem_alloc); if (test_bit(RFCOMM_TTY_ATTACHED, &dev->flags)) - tasklet_schedule(&dev->wakeup_task); + queue_work(system_nrt_wq, &dev->wakeup_task); rfcomm_dev_put(dev); } @@ -635,9 +636,10 @@ static void rfcomm_dev_modem_status(struct rfcomm_dlc *dlc, u8 v24_sig) } /* ---- TTY functions ---- */ -static void rfcomm_tty_wakeup(unsigned long arg) +static void rfcomm_tty_wakeup(struct work_struct *work) { - struct rfcomm_dev *dev = (void *) arg; + struct rfcomm_dev *dev = container_of(work, struct rfcomm_dev, + wakeup_task); struct tty_struct *tty = dev->tty; if (!tty) return; @@ -762,7 +764,7 @@ static void rfcomm_tty_close(struct tty_struct *tty, struct file *filp) rfcomm_dlc_close(dev->dlc, 0); clear_bit(RFCOMM_TTY_ATTACHED, &dev->flags); - tasklet_kill(&dev->wakeup_task); + cancel_work_sync(&dev->wakeup_task); rfcomm_dlc_lock(dev->dlc); tty->driver_data = NULL; -- cgit v1.2.3 From 13ea4015d37d2dbe597580898b5fafbe6f593f72 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 26 Oct 2011 10:43:18 +0200 Subject: Bluetooth: Ignore hci_unregister_dev return value Make all bluetooth drivers ignore the return value of hci_unregister_dev as it always returns 0. In the next step, hci_unregister_dev can be modified to return void. Some of the drivers already ignore the return value (including btusb), hence, this will increase consitency in the bluetooth drivers. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/bfusb.c | 4 +--- drivers/bluetooth/bluecard_cs.c | 4 +--- drivers/bluetooth/bt3c_cs.c | 4 +--- drivers/bluetooth/btuart_cs.c | 4 +--- drivers/bluetooth/dtl1_cs.c | 4 +--- drivers/bluetooth/hci_vhci.c | 5 +---- 6 files changed, 6 insertions(+), 19 deletions(-) diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 005919ab043c..6580d5057d26 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -750,9 +750,7 @@ static void bfusb_disconnect(struct usb_interface *intf) bfusb_close(hdev); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); } diff --git a/drivers/bluetooth/bluecard_cs.c b/drivers/bluetooth/bluecard_cs.c index aed1904ea67b..c6a0c6103743 100644 --- a/drivers/bluetooth/bluecard_cs.c +++ b/drivers/bluetooth/bluecard_cs.c @@ -844,9 +844,7 @@ static int bluecard_close(bluecard_info_t *info) /* Turn FPGA off */ outb(0x80, iobase + 0x30); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c index 4fc01949d399..0c97e5d514b6 100644 --- a/drivers/bluetooth/bt3c_cs.c +++ b/drivers/bluetooth/bt3c_cs.c @@ -636,9 +636,7 @@ static int bt3c_close(bt3c_info_t *info) bt3c_hci_close(hdev); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/btuart_cs.c b/drivers/bluetooth/btuart_cs.c index 526b61807d94..200b3a2877d6 100644 --- a/drivers/bluetooth/btuart_cs.c +++ b/drivers/bluetooth/btuart_cs.c @@ -565,9 +565,7 @@ static int btuart_close(btuart_info_t *info) spin_unlock_irqrestore(&(info->lock), flags); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c index 5e4c2de9fc3f..969bb22e493f 100644 --- a/drivers/bluetooth/dtl1_cs.c +++ b/drivers/bluetooth/dtl1_cs.c @@ -551,9 +551,7 @@ static int dtl1_close(dtl1_info_t *info) spin_unlock_irqrestore(&(info->lock), flags); - if (hci_unregister_dev(hdev) < 0) - BT_ERR("Can't unregister HCI device %s", hdev->name); - + hci_unregister_dev(hdev); hci_free_dev(hdev); return 0; diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 67c180c2c1e0..2e302a11ab55 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -264,10 +264,7 @@ static int vhci_release(struct inode *inode, struct file *file) struct vhci_data *data = file->private_data; struct hci_dev *hdev = data->hdev; - if (hci_unregister_dev(hdev) < 0) { - BT_ERR("Can't unregister HCI device %s", hdev->name); - } - + hci_unregister_dev(hdev); hci_free_dev(hdev); file->private_data = NULL; -- cgit v1.2.3 From 59735631d24e3463f139a21255e0db94bc59081e Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 26 Oct 2011 10:43:19 +0200 Subject: Bluetooth: Make hci_unregister_dev return void hci_unregister_dev cannot fail and always returns 0. The drivers already ignore the return value so we can safely make it return void. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 119b795b2850..967e18f72a38 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -554,7 +554,7 @@ struct hci_dev *hci_get_route(bdaddr_t *src, bdaddr_t *dst); struct hci_dev *hci_alloc_dev(void); void hci_free_dev(struct hci_dev *hdev); int hci_register_dev(struct hci_dev *hdev); -int hci_unregister_dev(struct hci_dev *hdev); +void hci_unregister_dev(struct hci_dev *hdev); int hci_suspend_dev(struct hci_dev *hdev); int hci_resume_dev(struct hci_dev *hdev); int hci_dev_open(__u16 dev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fdcbf8fc26ad..557ff90331b9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1541,7 +1541,7 @@ err: EXPORT_SYMBOL(hci_register_dev); /* Unregister HCI device */ -int hci_unregister_dev(struct hci_dev *hdev) +void hci_unregister_dev(struct hci_dev *hdev) { int i; @@ -1583,8 +1583,6 @@ int hci_unregister_dev(struct hci_dev *hdev) hci_dev_unlock_bh(hdev); __hci_dev_put(hdev); - - return 0; } EXPORT_SYMBOL(hci_unregister_dev); -- cgit v1.2.3 From c3eae82a844bb33e8182c7ee81828516b51ad642 Mon Sep 17 00:00:00 2001 From: Paul Fertser Date: Sat, 29 Oct 2011 21:52:49 +0400 Subject: Bluetooth: ath3k: output firmware filename when request_firmware failed This makes it much easier for the users to understand why the driver refuses to load when the firmware is unavailable. Signed-off-by: Paul Fertser Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/ath3k.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index db7cb8111fbe..39b25ac51c10 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -30,6 +30,7 @@ #include #define VERSION "1.0" +#define ATH3K_FIRMWARE "ath3k-1.fw" #define ATH3K_DNLOAD 0x01 #define ATH3K_GETSTATE 0x05 @@ -400,9 +401,15 @@ static int ath3k_probe(struct usb_interface *intf, return 0; } - if (request_firmware(&firmware, "ath3k-1.fw", &udev->dev) < 0) { - BT_ERR("Error loading firmware"); - return -EIO; + ret = request_firmware(&firmware, ATH3K_FIRMWARE, &udev->dev); + if (ret < 0) { + if (ret == -ENOENT) + BT_ERR("Firmware file \"%s\" not found", + ATH3K_FIRMWARE); + else + BT_ERR("Firmware file \"%s\" request failed (err=%d)", + ATH3K_FIRMWARE, ret); + return ret; } ret = ath3k_load_firmware(udev, firmware); @@ -441,4 +448,4 @@ MODULE_AUTHOR("Atheros Communications"); MODULE_DESCRIPTION("Atheros AR30xx firmware driver"); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("ath3k-1.fw"); +MODULE_FIRMWARE(ATH3K_FIRMWARE); -- cgit v1.2.3 From 0e8b207e8a4442f1a662e1a3827e61e40279630a Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 17 Oct 2011 14:35:32 +0300 Subject: Bluetooth: EFS: implement L2CAP config pending state Add L2CAP Config Pending state for EFS. Currently after receiving Config Response Pending respond with Config Response Success. ... > ACL data: handle 1 flags 0x02 dlen 16 L2CAP(s): Connect rsp: dcid 0x0040 scid 0x0040 result 0 status 0 Connection successful > ACL data: handle 1 flags 0x02 dlen 45 L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33 RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 1009) EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff, AccLat 0xffffffff, FlushTO 0x0000ffff) < ACL data: handle 1 flags 0x00 dlen 45 L2CAP(s): Config req: dcid 0x0040 flags 0x00 clen 33 RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 0, MTo 0, MPS 498) EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff, AccLat 0xffffffff, FlushTO 0x0000ffff) < ACL data: handle 1 flags 0x00 dlen 47 L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33 Pending MTU 672 RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498) EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff, AccLat 0xffffffff, FlushTO 0x0000ffff) > ACL data: handle 1 flags 0x02 dlen 47 L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 4 clen 33 Pending MTU 672 RFC 0x03 (Enhanced Retransmission, TxWin 63, MaxTx 3, RTo 2000, MTo 12000, MPS 498) EFS (Id 0x01, SerType Best Effort, MaxSDU 0xffff, SDUitime 0xffffffff, AccLat 0xffffffff, FlushTO 0x0000ffff) > ACL data: handle 1 flags 0x02 dlen 14 L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0 Success < ACL data: handle 1 flags 0x00 dlen 14 L2CAP(s): Config rsp: scid 0x0040 flags 0x00 result 0 clen 0 Success < ACL data: handle 1 flags 0x00 dlen 510 L2CAP(d): cid 0x0040 len 506 ext_ctrl 0x00010000 fcs 0xebe0 [psm 4113] I-frame: Start (len 672) TxSeq 0 ReqSeq 0 ... Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 3 +++ net/bluetooth/l2cap_core.c | 47 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index fddc82afeafc..38a561581169 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -246,6 +246,7 @@ struct l2cap_conf_rsp { #define L2CAP_CONF_UNACCEPT 0x0001 #define L2CAP_CONF_REJECT 0x0002 #define L2CAP_CONF_UNKNOWN 0x0003 +#define L2CAP_CONF_PENDING 0x0004 #define L2CAP_CONF_EFS_REJECT 0x0005 struct l2cap_conf_opt { @@ -505,6 +506,8 @@ enum { CONF_NO_FCS_RECV, CONF_STATE2_DEVICE, CONF_EWS_RECV, + CONF_LOC_CONF_PEND, + CONF_REM_CONF_PEND, }; #define L2CAP_CONF_MAX_CONF_REQ 2 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bda6da797734..c12d3bf08a42 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2239,6 +2239,11 @@ done: l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, sizeof(efs), (unsigned long) &efs); + } else { + /* Send PENDING Conf Rsp and mark state + local PENDING */ + result = L2CAP_CONF_PENDING; + set_bit(CONF_LOC_CONF_PEND, &chan->conf_state); } } @@ -2379,7 +2384,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi chan->mode = rfc.mode; - if (*result == L2CAP_CONF_SUCCESS) { + if (*result == L2CAP_CONF_SUCCESS || *result == L2CAP_CONF_PENDING) { switch (rfc.mode) { case L2CAP_MODE_ERTM: chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); @@ -2785,6 +2790,21 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr chan->num_conf_req++; } + /* Got Conf Rsp PENDING from remote side and asume we sent + Conf Rsp PENDING in the code above */ + if (test_bit(CONF_REM_CONF_PEND, &chan->conf_state) && + test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { + + /* check compatibility */ + + clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(chan, rsp, + L2CAP_CONF_SUCCESS, 0x0000), rsp); + } + unlock: bh_unlock_sock(sk); return 0; @@ -2814,8 +2834,33 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr switch (result) { case L2CAP_CONF_SUCCESS: l2cap_conf_rfc_get(chan, rsp->data, len); + clear_bit(CONF_REM_CONF_PEND, &chan->conf_state); break; + case L2CAP_CONF_PENDING: + set_bit(CONF_REM_CONF_PEND, &chan->conf_state); + + if (test_bit(CONF_LOC_CONF_PEND, &chan->conf_state)) { + char buf[64]; + + len = l2cap_parse_conf_rsp(chan, rsp->data, len, + buf, &result); + if (len < 0) { + l2cap_send_disconn_req(conn, chan, ECONNRESET); + goto done; + } + + /* check compatibility */ + + clear_bit(CONF_LOC_CONF_PEND, &chan->conf_state); + set_bit(CONF_OUTPUT_DONE, &chan->conf_state); + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, + l2cap_build_conf_rsp(chan, buf, + L2CAP_CONF_SUCCESS, 0x0000), buf); + } + goto done; + case L2CAP_CONF_UNACCEPT: if (chan->num_conf_rsp <= L2CAP_CONF_MAX_CONF_RSP) { char req[64]; -- cgit v1.2.3 From 3e6b3b95f26e1253cd429cf949b9c41598ba3033 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Tue, 1 Nov 2011 14:06:23 -0200 Subject: Bluetooth: small styles clean ups to l2cap_core.c Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index c12d3bf08a42..7d663bd3bc5d 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2237,11 +2237,10 @@ done: return -ECONNREFUSED; l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, - sizeof(efs), + sizeof(efs), (unsigned long) &efs); } else { - /* Send PENDING Conf Rsp and mark state - local PENDING */ + /* Send PENDING Conf Rsp */ result = L2CAP_CONF_PENDING; set_bit(CONF_LOC_CONF_PEND, &chan->conf_state); } @@ -2373,8 +2372,8 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi case L2CAP_CONF_EWS: chan->tx_win = min_t(u16, val, L2CAP_DEFAULT_EXT_WINDOW); - l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, - 2, chan->tx_win); + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, + chan->tx_win); break; } } @@ -2801,7 +2800,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr set_bit(CONF_OUTPUT_DONE, &chan->conf_state); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, rsp, + l2cap_build_conf_rsp(chan, rsp, L2CAP_CONF_SUCCESS, 0x0000), rsp); } @@ -2856,7 +2855,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hdr set_bit(CONF_OUTPUT_DONE, &chan->conf_state); l2cap_send_cmd(conn, cmd->ident, L2CAP_CONF_RSP, - l2cap_build_conf_rsp(chan, buf, + l2cap_build_conf_rsp(chan, buf, L2CAP_CONF_SUCCESS, 0x0000), buf); } goto done; -- cgit v1.2.3 From c6337ea6e5771a3c51c1ac7295a16f390220abe1 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Thu, 20 Oct 2011 17:02:44 +0300 Subject: Bluetooth: remove magic offset and size make code readable by removing magic numbers Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 7d663bd3bc5d..18b0bba8630f 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3021,7 +3021,7 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); - memcpy(buf + 4, l2cap_fixed_chan, 8); + memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); l2cap_send_cmd(conn, cmd->ident, L2CAP_INFO_RSP, sizeof(buf), buf); } else { -- cgit v1.2.3 From 457f48507deb0e8c8dd299c7d8dce7c2c0e291e8 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 31 Oct 2011 16:17:21 +0200 Subject: Bluetooth: correct debug output l2cap_set_timer function prints sk instead of chan pointer. Signed-off-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 18b0bba8630f..76210cd6d3ea 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -220,7 +220,7 @@ static u16 l2cap_alloc_cid(struct l2cap_conn *conn) static void l2cap_set_timer(struct l2cap_chan *chan, struct timer_list *timer, long timeout) { - BT_DBG("chan %p state %d timeout %ld", chan->sk, chan->state, timeout); + BT_DBG("chan %p state %d timeout %ld", chan, chan->state, timeout); if (!mod_timer(timer, jiffies + msecs_to_jiffies(timeout))) chan_hold(chan); -- cgit v1.2.3 From 8035ded466049ca2fe8c04564a0fa00f222abe3f Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 1 Nov 2011 10:58:56 +0200 Subject: Bluetooth: replace list_for_each with list_for_each_entry whenever possible When all items in the list have the same type there is no much of a point to use list_for_each except if you want to use the list pointer itself. Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/bnep/core.c | 13 ++++--------- net/bluetooth/cmtp/core.c | 13 ++++--------- net/bluetooth/hci_conn.c | 14 ++++---------- net/bluetooth/hci_core.c | 46 ++++++++++++--------------------------------- net/bluetooth/hci_sysfs.c | 18 ++++-------------- net/bluetooth/hidp/core.c | 1 + net/bluetooth/mgmt.c | 32 ++++++++++--------------------- net/bluetooth/rfcomm/core.c | 14 +++++--------- net/bluetooth/rfcomm/tty.c | 20 +++++++------------- 9 files changed, 51 insertions(+), 120 deletions(-) diff --git a/net/bluetooth/bnep/core.c b/net/bluetooth/bnep/core.c index 91bcd3a961ec..a6cd856046ab 100644 --- a/net/bluetooth/bnep/core.c +++ b/net/bluetooth/bnep/core.c @@ -65,15 +65,13 @@ static DECLARE_RWSEM(bnep_session_sem); static struct bnep_session *__bnep_get_session(u8 *dst) { struct bnep_session *s; - struct list_head *p; BT_DBG(""); - list_for_each(p, &bnep_session_list) { - s = list_entry(p, struct bnep_session, list); + list_for_each_entry(s, &bnep_session_list, list) if (!compare_ether_addr(dst, s->eh.h_source)) return s; - } + return NULL; } @@ -667,17 +665,14 @@ static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s) int bnep_get_connlist(struct bnep_connlist_req *req) { - struct list_head *p; + struct bnep_session *s; int err = 0, n = 0; down_read(&bnep_session_sem); - list_for_each(p, &bnep_session_list) { - struct bnep_session *s; + list_for_each_entry(s, &bnep_session_list, list) { struct bnep_conninfo ci; - s = list_entry(p, struct bnep_session, list); - __bnep_copy_ci(&ci, s); if (copy_to_user(req->ci, &ci, sizeof(ci))) { diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c index 7d00ddf9e9dc..9e8940b24bba 100644 --- a/net/bluetooth/cmtp/core.c +++ b/net/bluetooth/cmtp/core.c @@ -53,15 +53,13 @@ static LIST_HEAD(cmtp_session_list); static struct cmtp_session *__cmtp_get_session(bdaddr_t *bdaddr) { struct cmtp_session *session; - struct list_head *p; BT_DBG(""); - list_for_each(p, &cmtp_session_list) { - session = list_entry(p, struct cmtp_session, list); + list_for_each_entry(session, &cmtp_session_list, list) if (!bacmp(bdaddr, &session->bdaddr)) return session; - } + return NULL; } @@ -431,19 +429,16 @@ int cmtp_del_connection(struct cmtp_conndel_req *req) int cmtp_get_connlist(struct cmtp_connlist_req *req) { - struct list_head *p; + struct cmtp_session *session; int err = 0, n = 0; BT_DBG(""); down_read(&cmtp_session_sem); - list_for_each(p, &cmtp_session_list) { - struct cmtp_session *session; + list_for_each_entry(session, &cmtp_session_list, list) { struct cmtp_conninfo ci; - session = list_entry(p, struct cmtp_session, list); - __cmtp_copy_session(session, &ci); if (copy_to_user(req->ci, &ci, sizeof(ci))) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index c1c597e3e198..6e98ff3da2a4 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -453,16 +453,13 @@ int hci_conn_del(struct hci_conn *conn) struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) { int use_src = bacmp(src, BDADDR_ANY); - struct hci_dev *hdev = NULL; - struct list_head *p; + struct hci_dev *hdev = NULL, *d; BT_DBG("%s -> %s", batostr(src), batostr(dst)); read_lock_bh(&hci_dev_list_lock); - list_for_each(p, &hci_dev_list) { - struct hci_dev *d = list_entry(p, struct hci_dev, list); - + list_for_each_entry(d, &hci_dev_list, list) { if (!test_bit(HCI_UP, &d->flags) || test_bit(HCI_RAW, &d->flags)) continue; @@ -855,10 +852,10 @@ EXPORT_SYMBOL(hci_conn_put_device); int hci_get_conn_list(void __user *arg) { + register struct hci_conn *c; struct hci_conn_list_req req, *cl; struct hci_conn_info *ci; struct hci_dev *hdev; - struct list_head *p; int n = 0, size, err; if (copy_from_user(&req, arg, sizeof(req))) @@ -882,10 +879,7 @@ int hci_get_conn_list(void __user *arg) ci = cl->conn_info; hci_dev_lock_bh(hdev); - list_for_each(p, &hdev->conn_hash.list) { - register struct hci_conn *c; - c = list_entry(p, struct hci_conn, list); - + list_for_each_entry(c, &hdev->conn_hash.list, list) { bacpy(&(ci + n)->bdaddr, &c->dst); (ci + n)->handle = c->handle; (ci + n)->type = c->type; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 557ff90331b9..f04f2eca2028 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -319,8 +319,7 @@ static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) * Device is held on return. */ struct hci_dev *hci_dev_get(int index) { - struct hci_dev *hdev = NULL; - struct list_head *p; + struct hci_dev *hdev = NULL, *d; BT_DBG("%d", index); @@ -328,8 +327,7 @@ struct hci_dev *hci_dev_get(int index) return NULL; read_lock(&hci_dev_list_lock); - list_for_each(p, &hci_dev_list) { - struct hci_dev *d = list_entry(p, struct hci_dev, list); + list_for_each_entry(d, &hci_dev_list, list) { if (d->id == index) { hdev = hci_dev_hold(d); break; @@ -794,9 +792,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) int hci_get_dev_list(void __user *arg) { + struct hci_dev *hdev; struct hci_dev_list_req *dl; struct hci_dev_req *dr; - struct list_head *p; int n = 0, size, err; __u16 dev_num; @@ -815,11 +813,7 @@ int hci_get_dev_list(void __user *arg) dr = dl->dev_req; read_lock_bh(&hci_dev_list_lock); - list_for_each(p, &hci_dev_list) { - struct hci_dev *hdev; - - hdev = list_entry(p, struct hci_dev, list); - + list_for_each_entry(hdev, &hci_dev_list, list) { hci_del_off_timer(hdev); if (!test_bit(HCI_MGMT, &hdev->flags)) @@ -1008,16 +1002,11 @@ int hci_link_keys_clear(struct hci_dev *hdev) struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr) { - struct list_head *p; - - list_for_each(p, &hdev->link_keys) { - struct link_key *k; - - k = list_entry(p, struct link_key, list); + struct link_key *k; + list_for_each_entry(k, &hdev->link_keys, list) if (bacmp(bdaddr, &k->bdaddr) == 0) return k; - } return NULL; } @@ -1280,16 +1269,11 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *hash, struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, bdaddr_t *bdaddr) { - struct list_head *p; - - list_for_each(p, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(p, struct bdaddr_list, list); + struct bdaddr_list *b; + list_for_each_entry(b, &hdev->blacklist, list) if (bacmp(bdaddr, &b->bdaddr) == 0) return b; - } return NULL; } @@ -2031,16 +2015,12 @@ EXPORT_SYMBOL(hci_send_sco); static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int *quote) { struct hci_conn_hash *h = &hdev->conn_hash; - struct hci_conn *conn = NULL; + struct hci_conn *conn = NULL, *c; int num = 0, min = ~0; - struct list_head *p; /* We don't have to lock device here. Connections are always * added and removed with TX task disabled. */ - list_for_each(p, &h->list) { - struct hci_conn *c; - c = list_entry(p, struct hci_conn, list); - + list_for_each_entry(c, &h->list, list) { if (c->type != type || skb_queue_empty(&c->data_q)) continue; @@ -2089,14 +2069,12 @@ static inline struct hci_conn *hci_low_sent(struct hci_dev *hdev, __u8 type, int static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) { struct hci_conn_hash *h = &hdev->conn_hash; - struct list_head *p; - struct hci_conn *c; + struct hci_conn *c; BT_ERR("%s link tx timeout", hdev->name); /* Kill stalled connections */ - list_for_each(p, &h->list) { - c = list_entry(p, struct hci_conn, list); + list_for_each_entry(c, &h->list, list) { if (c->type == type && c->sent) { BT_ERR("%s killing stalled connection %s", hdev->name, batostr(&c->dst)); diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 1f9f8769e130..f8e6aa386cef 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -435,17 +435,12 @@ static const struct file_operations inquiry_cache_fops = { static int blacklist_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; - struct list_head *l; + struct bdaddr_list *b; hci_dev_lock_bh(hdev); - list_for_each(l, &hdev->blacklist) { - struct bdaddr_list *b; - - b = list_entry(l, struct bdaddr_list, list); - + list_for_each_entry(b, &hdev->blacklist, list) seq_printf(f, "%s\n", batostr(&b->bdaddr)); - } hci_dev_unlock_bh(hdev); @@ -484,17 +479,12 @@ static void print_bt_uuid(struct seq_file *f, u8 *uuid) static int uuids_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; - struct list_head *l; + struct bt_uuid *uuid; hci_dev_lock_bh(hdev); - list_for_each(l, &hdev->uuids) { - struct bt_uuid *uuid; - - uuid = list_entry(l, struct bt_uuid, list); - + list_for_each_entry(uuid, &hdev->uuids, list) print_bt_uuid(f, uuid->uuid); - } hci_dev_unlock_bh(hdev); diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 217ef4761560..2efd6cc58b66 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -88,6 +88,7 @@ static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) if (!bacmp(bdaddr, &session->bdaddr)) return session; } + return NULL; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9ffd7c3dbb3e..7809aa979358 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -123,6 +123,7 @@ static int read_index_list(struct sock *sk) { struct mgmt_rp_read_index_list *rp; struct list_head *p; + struct hci_dev *d; size_t rp_len; u16 count; int i, err; @@ -146,9 +147,7 @@ static int read_index_list(struct sock *sk) put_unaligned_le16(count, &rp->num_controllers); i = 0; - list_for_each(p, &hci_dev_list) { - struct hci_dev *d = list_entry(p, struct hci_dev, list); - + list_for_each_entry(d, &hci_dev_list, list) { hci_del_off_timer(d); set_bit(HCI_MGMT, &d->flags); @@ -277,13 +276,9 @@ static void mgmt_pending_foreach(u16 opcode, int index, static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) { - struct list_head *p; - - list_for_each(p, &cmd_list) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); + struct pending_cmd *cmd; + list_for_each_entry(cmd, &cmd_list, list) { if (cmd->opcode != opcode) continue; @@ -592,7 +587,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) u16 eir_len = 0; u16 uuid16_list[HCI_MAX_EIR_LENGTH / sizeof(u16)]; int i, truncated = 0; - struct list_head *p; + struct bt_uuid *uuid; size_t name_len; name_len = strlen(hdev->dev_name); @@ -617,8 +612,7 @@ static void create_eir(struct hci_dev *hdev, u8 *data) memset(uuid16_list, 0, sizeof(uuid16_list)); /* Group all UUID16 types */ - list_for_each(p, &hdev->uuids) { - struct bt_uuid *uuid = list_entry(p, struct bt_uuid, list); + list_for_each_entry(uuid, &hdev->uuids, list) { u16 uuid16; uuid16 = get_uuid16(uuid->uuid); @@ -1069,6 +1063,7 @@ static int get_connections(struct sock *sk, u16 index) { struct mgmt_rp_get_connections *rp; struct hci_dev *hdev; + struct hci_conn *c; struct list_head *p; size_t rp_len; u16 count; @@ -1097,11 +1092,8 @@ static int get_connections(struct sock *sk, u16 index) put_unaligned_le16(count, &rp->conn_count); i = 0; - list_for_each(p, &hdev->conn_hash.list) { - struct hci_conn *c = list_entry(p, struct hci_conn, list); - + list_for_each_entry(c, &hdev->conn_hash.list, list) bacpy(&rp->conn[i++], &c->dst); - } err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len); @@ -1270,13 +1262,9 @@ static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, static inline struct pending_cmd *find_pairing(struct hci_conn *conn) { struct hci_dev *hdev = conn->hdev; - struct list_head *p; - - list_for_each(p, &cmd_list) { - struct pending_cmd *cmd; - - cmd = list_entry(p, struct pending_cmd, list); + struct pending_cmd *cmd; + list_for_each_entry(cmd, &cmd_list, list) { if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 38b618c96de6..3d35eba6d0cb 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -377,13 +377,11 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) { struct rfcomm_dlc *d; - struct list_head *p; - list_for_each(p, &s->dlcs) { - d = list_entry(p, struct rfcomm_dlc, list); + list_for_each_entry(d, &s->dlcs, list) if (d->dlci == dlci) return d; - } + return NULL; } @@ -2115,15 +2113,13 @@ static struct hci_cb rfcomm_cb = { static int rfcomm_dlc_debugfs_show(struct seq_file *f, void *x) { struct rfcomm_session *s; - struct list_head *pp, *p; rfcomm_lock(); - list_for_each(p, &session_list) { - s = list_entry(p, struct rfcomm_session, list); - list_for_each(pp, &s->dlcs) { + list_for_each_entry(s, &session_list, list) { + struct rfcomm_dlc *d; + list_for_each_entry(d, &s->dlcs, list) { struct sock *sk = s->sock->sk; - struct rfcomm_dlc *d = list_entry(pp, struct rfcomm_dlc, list); seq_printf(f, "%s %s %ld %d %d %d %d\n", batostr(&bt_sk(sk)->src), diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 947f1b3afd15..fa8f4de53b99 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -134,13 +134,10 @@ static inline void rfcomm_dev_put(struct rfcomm_dev *dev) static struct rfcomm_dev *__rfcomm_dev_get(int id) { struct rfcomm_dev *dev; - struct list_head *p; - list_for_each(p, &rfcomm_dev_list) { - dev = list_entry(p, struct rfcomm_dev, list); + list_for_each_entry(dev, &rfcomm_dev_list, list) if (dev->id == id) return dev; - } return NULL; } @@ -198,7 +195,7 @@ static DEVICE_ATTR(channel, S_IRUGO, show_channel, NULL); static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) { - struct rfcomm_dev *dev; + struct rfcomm_dev *dev, *entry; struct list_head *head = &rfcomm_dev_list, *p; int err = 0; @@ -213,8 +210,8 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) if (req->dev_id < 0) { dev->id = 0; - list_for_each(p, &rfcomm_dev_list) { - if (list_entry(p, struct rfcomm_dev, list)->id != dev->id) + list_for_each_entry(entry, &rfcomm_dev_list, list) { + if (entry->id != dev->id) break; dev->id++; @@ -223,9 +220,7 @@ static int rfcomm_dev_add(struct rfcomm_dev_req *req, struct rfcomm_dlc *dlc) } else { dev->id = req->dev_id; - list_for_each(p, &rfcomm_dev_list) { - struct rfcomm_dev *entry = list_entry(p, struct rfcomm_dev, list); - + list_for_each_entry(entry, &rfcomm_dev_list, list) { if (entry->id == dev->id) { err = -EADDRINUSE; goto out; @@ -456,9 +451,9 @@ static int rfcomm_release_dev(void __user *arg) static int rfcomm_get_dev_list(void __user *arg) { + struct rfcomm_dev *dev; struct rfcomm_dev_list_req *dl; struct rfcomm_dev_info *di; - struct list_head *p; int n = 0, size, err; u16 dev_num; @@ -480,8 +475,7 @@ static int rfcomm_get_dev_list(void __user *arg) read_lock_bh(&rfcomm_dev_lock); - list_for_each(p, &rfcomm_dev_list) { - struct rfcomm_dev *dev = list_entry(p, struct rfcomm_dev, list); + list_for_each_entry(dev, &rfcomm_dev_list, list) { if (test_bit(RFCOMM_TTY_RELEASED, &dev->flags)) continue; (di + n)->id = dev->id; -- cgit v1.2.3 From 5e59b791c3561e2fbb4aee17df3505ad25c16b7a Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 1 Nov 2011 10:58:57 +0200 Subject: Bluetooth: set skbuffer priority based on L2CAP socket priority This uses SO_PRIORITY to set the skbuffer priority field Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 +++ include/net/bluetooth/l2cap.h | 3 ++- net/bluetooth/l2cap_core.c | 27 ++++++++++++++++++++------- net/bluetooth/l2cap_sock.c | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 967e18f72a38..9285a650949c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -32,6 +32,9 @@ #define HCI_PROTO_L2CAP 0 #define HCI_PROTO_SCO 1 +/* HCI priority */ +#define HCI_PRIO_MAX 7 + /* HCI Core structures */ struct inquiry_data { bdaddr_t bdaddr; diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 38a561581169..c10bf1db0abb 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -747,7 +747,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk); void l2cap_chan_close(struct l2cap_chan *chan, int reason); void l2cap_chan_destroy(struct l2cap_chan *chan); int l2cap_chan_connect(struct l2cap_chan *chan); -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, + u32 priority); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); #endif /* __L2CAP_H */ diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 76210cd6d3ea..ac2c41ada0fe 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -564,6 +564,7 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, flags = ACL_START; bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; + skb->priority = HCI_PRIO_MAX; hci_send_acl(conn->hcon, skb, flags); } @@ -1265,7 +1266,8 @@ static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) struct hci_conn *hcon = chan->conn->hcon; u16 flags; - BT_DBG("chan %p, skb %p len %d", chan, skb, skb->len); + BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, + skb->priority); if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && lmp_no_flush_capable(hcon->hdev)) @@ -1483,6 +1485,8 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) return -EFAULT; + (*frag)->priority = skb->priority; + sent += count; len -= count; @@ -1492,7 +1496,9 @@ static inline int l2cap_skbuff_fromiovec(struct sock *sk, struct msghdr *msg, in return sent; } -static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, + struct msghdr *msg, size_t len, + u32 priority) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1500,7 +1506,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; - BT_DBG("sk %p len %d", sk, (int)len); + BT_DBG("sk %p len %d priority %u", sk, (int)len, priority); count = min_t(unsigned int, (conn->mtu - hlen), len); skb = bt_skb_send_alloc(sk, count + hlen, @@ -1508,6 +1514,8 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct if (!skb) return ERR_PTR(err); + skb->priority = priority; + /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -1522,7 +1530,9 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, struct return skb; } -static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, + struct msghdr *msg, size_t len, + u32 priority) { struct sock *sk = chan->sk; struct l2cap_conn *conn = chan->conn; @@ -1538,6 +1548,8 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, struct ms if (!skb) return ERR_PTR(err); + skb->priority = priority; + /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -1651,7 +1663,8 @@ static int l2cap_sar_segment_sdu(struct l2cap_chan *chan, struct msghdr *msg, si return size; } -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, + u32 priority) { struct sk_buff *skb; u32 control; @@ -1659,7 +1672,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) /* Connectionless channel */ if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { - skb = l2cap_create_connless_pdu(chan, msg, len); + skb = l2cap_create_connless_pdu(chan, msg, len, priority); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1674,7 +1687,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) return -EMSGSIZE; /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len); + skb = l2cap_create_basic_pdu(chan, msg, len, priority); if (IS_ERR(skb)) return PTR_ERR(skb); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 836d12e66a38..646aefc4f1d7 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -721,7 +721,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms return -ENOTCONN; } - err = l2cap_chan_send(chan, msg, len); + err = l2cap_chan_send(chan, msg, len, sk->sk_priority); release_sock(sk); return err; -- cgit v1.2.3 From 262038fcb2a50e9b5553243452918fda08cdf83d Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Tue, 1 Nov 2011 10:58:58 +0200 Subject: Bluetooth: make use sk_priority to priritize RFCOMM packets Signed-off-by: Luiz Augusto von Dentz Signed-off-by: Gustavo F. Padovan --- net/bluetooth/rfcomm/core.c | 51 +++++++++++++++++++++++++++++++-------------- net/bluetooth/rfcomm/sock.c | 2 ++ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 3d35eba6d0cb..24bf96188cc5 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -65,7 +65,8 @@ static DEFINE_MUTEX(rfcomm_mutex); static LIST_HEAD(session_list); -static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len); +static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len, + u32 priority); 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); @@ -747,19 +748,34 @@ 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) +static int rfcomm_send_frame(struct rfcomm_session *s, u8 *data, int len, + u32 priority) { struct socket *sock = s->sock; + struct sock *sk = sock->sk; struct kvec iv = { data, len }; struct msghdr msg; - BT_DBG("session %p len %d", s, len); + 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); + } memset(&msg, 0, sizeof(msg)); return kernel_sendmsg(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); +} + static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) { struct rfcomm_cmd cmd; @@ -771,7 +787,7 @@ static int rfcomm_send_sabm(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) @@ -785,7 +801,7 @@ static int rfcomm_send_ua(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) @@ -799,7 +815,7 @@ static int rfcomm_send_disc(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_queue_disc(struct rfcomm_dlc *d) @@ -813,6 +829,8 @@ 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); @@ -835,7 +853,7 @@ static int rfcomm_send_dm(struct rfcomm_session *s, u8 dlci) cmd.len = __len8(0); cmd.fcs = __fcs2((u8 *) &cmd); - return rfcomm_send_frame(s, (void *) &cmd, sizeof(cmd)); + return rfcomm_send_cmd(s, &cmd); } static int rfcomm_send_nsc(struct rfcomm_session *s, int cr, u8 type) @@ -860,7 +878,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); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_pn(struct rfcomm_session *s, int cr, struct rfcomm_dlc *d) @@ -902,7 +920,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); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } int rfcomm_send_rpn(struct rfcomm_session *s, int cr, u8 dlci, @@ -940,7 +958,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); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_rls(struct rfcomm_session *s, int cr, u8 dlci, u8 status) @@ -967,7 +985,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); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_msc(struct rfcomm_session *s, int cr, u8 dlci, u8 v24_sig) @@ -994,7 +1012,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); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) @@ -1016,7 +1034,7 @@ static int rfcomm_send_fcoff(struct rfcomm_session *s, int cr) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) @@ -1038,7 +1056,7 @@ static int rfcomm_send_fcon(struct rfcomm_session *s, int cr) *ptr = __fcs(buf); ptr++; - return rfcomm_send_frame(s, buf, ptr - buf); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static int rfcomm_send_test(struct rfcomm_session *s, int cr, u8 *pattern, int len) @@ -1089,7 +1107,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); + return rfcomm_send_frame(s, buf, ptr - buf, HCI_PRIO_MAX); } static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) @@ -1767,7 +1785,8 @@ 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); + err = rfcomm_send_frame(d->session, skb->data, skb->len, + skb->priority); if (err < 0) { skb_queue_head(&d->tx_queue, skb); break; diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 482722bbc7a0..40988e2dc8ef 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -597,6 +597,8 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock, break; } + skb->priority = sk->sk_priority; + err = rfcomm_dlc_send(d, skb); if (err < 0) { kfree_skb(skb); -- cgit v1.2.3 From 164a6e78990f6201dc3105ff88335ca91392a427 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Nov 2011 17:06:44 +0200 Subject: Bluetooth: Fix command complete/status for discovery commands This patch adds the necessary code to send proper command status or command complete events to the start/stop discovery management commands. Before this patch these events were completely missing. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 2 ++ net/bluetooth/mgmt.c | 27 +++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9285a650949c..5a9db9a4b439 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -887,6 +887,7 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, u8 *eir); int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); +int mgmt_inquiry_failed(u16 index, u8 status); int mgmt_discovering(u16 index, u8 discovering); int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr); int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 41967fec12b7..d8fa65709aed 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -979,6 +979,8 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) if (status) { hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) + mgmt_inquiry_failed(hdev->id, status); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7809aa979358..38220a2dc31e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2339,8 +2339,35 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL); } +int mgmt_inquiry_failed(u16 index, u8 status) +{ + struct pending_cmd *cmd; + int err; + + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, index); + if (!cmd) + return -ENOENT; + + err = cmd_status(cmd->sk, index, cmd->opcode, status); + mgmt_pending_remove(cmd); + + return err; +} + int mgmt_discovering(u16 index, u8 discovering) { + struct pending_cmd *cmd; + + if (discovering) + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, index); + else + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); + + if (cmd != NULL) { + cmd_complete(cmd->sk, index, cmd->opcode, NULL, 0); + mgmt_pending_remove(cmd); + } + return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, sizeof(discovering), NULL); } -- cgit v1.2.3 From db54467a89266c02f9ce6c6db1d193365cff62a4 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Mon, 26 Sep 2011 14:19:47 +0200 Subject: Bluetooth: rfcomm: Fix sleep in invalid context in rfcomm_security_cfm This was triggered by turning off encryption on ACL link when rfcomm was using high security. rfcomm_security_cfm (which is called from rx task) was closing DLC and this involves sending disconnect message (and locking socket). Move closing DLC to rfcomm_process_dlcs and only flag DLC for closure in rfcomm_security_cfm. BUG: sleeping function called from invalid context at net/core/sock.c:2032 in_atomic(): 1, irqs_disabled(): 0, pid: 1788, name: kworker/0:3 [] (unwind_backtrace+0x0/0x108) from [] (dump_stack+0x20/0x24) [] (dump_stack+0x20/0x24) from [] (__might_sleep+0x110/0x12c) [] (__might_sleep+0x110/0x12c) from [] (lock_sock_nested+0x2c/0x64) [] (lock_sock_nested+0x2c/0x64) from [] (l2cap_sock_sendmsg+0x58/0xcc) [] (l2cap_sock_sendmsg+0x58/0xcc) from [] (sock_sendmsg+0xb0/0xd0) [] (sock_sendmsg+0xb0/0xd0) from [] (kernel_sendmsg+0x3c/0x44) [] (kernel_sendmsg+0x3c/0x44) from [] (rfcomm_send_frame+0x50/0x58) [] (rfcomm_send_frame+0x50/0x58) from [] (rfcomm_send_disc+0x78/0x80) [] (rfcomm_send_disc+0x78/0x80) from [] (__rfcomm_dlc_close+0x2d0/0x2fc) [] (__rfcomm_dlc_close+0x2d0/0x2fc) from [] (rfcomm_security_cfm+0x140/0x1e0) [] (rfcomm_security_cfm+0x140/0x1e0) from [] (hci_event_packet+0x1ce8/0x4d84) [] (hci_event_packet+0x1ce8/0x4d84) from [] (hci_rx_task+0x1d0/0x2d0) [] (hci_rx_task+0x1d0/0x2d0) from [] (tasklet_action+0x138/0x1e4) [] (tasklet_action+0x138/0x1e4) from [] (__do_softirq+0xcc/0x274) [] (__do_softirq+0xcc/0x274) from [] (do_softirq+0x60/0x6c) [] (do_softirq+0x60/0x6c) from [] (local_bh_enable_ip+0xc8/0xd4) [] (local_bh_enable_ip+0xc8/0xd4) from [] (_raw_spin_unlock_bh+0x48/0x4c) [] (_raw_spin_unlock_bh+0x48/0x4c) from [] (data_from_chip+0xf4/0xaec) [] (data_from_chip+0xf4/0xaec) from [] (send_skb_to_core+0x40/0x178) [] (send_skb_to_core+0x40/0x178) from [] (cg2900_hu_receive+0x15c/0x2d0) [] (cg2900_hu_receive+0x15c/0x2d0) from [] (hci_uart_tty_receive+0x74/0xa0) [] (hci_uart_tty_receive+0x74/0xa0) from [] (flush_to_ldisc+0x188/0x198) [] (flush_to_ldisc+0x188/0x198) from [] (process_one_work+0x144/0x4b8) [] (process_one_work+0x144/0x4b8) from [] (worker_thread+0x198/0x468) [] (worker_thread+0x198/0x468) from [] (kthread+0x98/0xa0) [] (kthread+0x98/0xa0) from [] (kernel_thread_exit+0x0/0x8) Signed-off-by: Szymon Janc Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/rfcomm.h | 1 + net/bluetooth/rfcomm/core.c | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h index d5eee2093b1e..e2e3ecad1008 100644 --- a/include/net/bluetooth/rfcomm.h +++ b/include/net/bluetooth/rfcomm.h @@ -211,6 +211,7 @@ struct rfcomm_dlc { #define RFCOMM_AUTH_ACCEPT 6 #define RFCOMM_AUTH_REJECT 7 #define RFCOMM_DEFER_SETUP 8 +#define RFCOMM_ENC_DROP 9 /* Scheduling flags and events */ #define RFCOMM_SCHED_WAKEUP 31 diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 24bf96188cc5..8743f369ed3f 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -1819,6 +1819,11 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s) continue; } + if (test_bit(RFCOMM_ENC_DROP, &d->flags)) { + __rfcomm_dlc_close(d, ECONNREFUSED); + continue; + } + if (test_and_clear_bit(RFCOMM_AUTH_ACCEPT, &d->flags)) { rfcomm_dlc_clear_timer(d); if (d->out) { @@ -2094,7 +2099,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) { rfcomm_dlc_clear_timer(d); if (status || encrypt == 0x00) { - __rfcomm_dlc_close(d, ECONNREFUSED); + set_bit(RFCOMM_ENC_DROP, &d->flags); continue; } } @@ -2105,7 +2110,7 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT); continue; } else if (d->sec_level == BT_SECURITY_HIGH) { - __rfcomm_dlc_close(d, ECONNREFUSED); + set_bit(RFCOMM_ENC_DROP, &d->flags); continue; } } -- cgit v1.2.3 From 43611a7b16038753e0510dfb0c038c80a10c80c3 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Mon, 17 Oct 2011 23:05:49 +0200 Subject: Bluetooth: Increase HCI reset timeout in hci_dev_do_close I've noticed that my CSR usb dongle was not working if it was plugged in when PC was booting. It looks like I get two HCI reset command complete events (see hcidump logs below). The root cause is reset called from off_timer. Timeout for this reset to complete is set to 250ms and my bt dongle requires more time for replying with command complete event. After that, chip seems to reply with reset command complete event for next non-reset command. Attached patch increase mentioned timeout to HCI_INIT_TIMEOUT, this value is already used for timeouting hci_reset_req in hci_dev_reset(). This might also be related to BT not working after suspend that was reported here some time ago. Hcidump log: 2011-09-12 23:13:27.379465 < HCI Command: Reset (0x03|0x0003) plen 0 2011-09-12 23:13:27.380797 > HCI Event: Command Complete (0x0e) plen 4 Reset (0x03|0x0003) ncmd 1 status 0x00 2011-09-12 23:13:27.380859 < HCI Command: Read Local Supported Features (0x04|0x000 3) plen 0 2011-09-12 23:13:27.760789 > HCI Event: Command Complete (0x0e) plen 4 Reset (0x03|0x0003) ncmd 1 status 0x00 2011-09-12 23:13:27.760831 < HCI Command: Read Local Version Information (0x04|0x00 01) plen 0 2011-09-12 23:13:27.764780 > HCI Event: Command Complete (0x0e) plen 12 Read Local Version Information (0x04|0x0001) ncmd 1 status 0x00 HCI Version: 1.1 (0x1) HCI Revision: 0x36f LMP Version: 1.1 (0x1) LMP Subversion: 0x36f Manufacturer: Cambridge Silicon Radio (10) Signed-off-by: Szymon Janc Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f04f2eca2028..f2ec434971f6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -611,7 +611,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (!test_bit(HCI_RAW, &hdev->flags)) { set_bit(HCI_INIT, &hdev->flags); __hci_request(hdev, hci_reset_req, 0, - msecs_to_jiffies(250)); + msecs_to_jiffies(HCI_INIT_TIMEOUT)); clear_bit(HCI_INIT, &hdev->flags); } -- cgit v1.2.3 From df164df9a77979d1774ede353988c1a62584594b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 24 Oct 2011 22:36:26 +0200 Subject: Bluetooth: Set HCI_MGMT flag only in read_controller_info The HCI_MGMT flag should only be set when user space requests the full controller information. This way we avoid potential issues with setting change events ariving before the actual read_controller_info command finishes. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 38220a2dc31e..cbc8a6dfa5ed 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -150,8 +150,6 @@ static int read_index_list(struct sock *sk) list_for_each_entry(d, &hci_dev_list, list) { hci_del_off_timer(d); - set_bit(HCI_MGMT, &d->flags); - if (test_bit(HCI_SETUP, &d->flags)) continue; -- cgit v1.2.3 From 52a1020e80beece986002f673eca24dae6255b55 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 25 Oct 2011 12:09:52 +0200 Subject: Bluetooth: ath3k: Use GFP_KERNEL instead of GFP_ATOMIC We are allowed to sleep here so no need to use GFP_ATOMIC. The caller (ath3k_probe) calls request_firmware() which definitely sleeps. Hence, we should avoid using GFP_ATOMIC. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/ath3k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index 39b25ac51c10..1622772f802d 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -106,7 +106,7 @@ static int ath3k_load_firmware(struct usb_device *udev, pipe = usb_sndctrlpipe(udev, 0); - send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC); + send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); if (!send_buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; @@ -177,7 +177,7 @@ static int ath3k_load_fwfile(struct usb_device *udev, count = firmware->size; - send_buf = kmalloc(BULK_SIZE, GFP_ATOMIC); + send_buf = kmalloc(BULK_SIZE, GFP_KERNEL); if (!send_buf) { BT_ERR("Can't allocate memory chunk for firmware"); return -ENOMEM; -- cgit v1.2.3 From 844e4b76cc4806827024cccf35a9beaf13d27f3d Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 26 Oct 2011 11:13:13 +0200 Subject: Bluetooth: bcm203x: Fix race condition on disconnect When disconnecting a bcm203x device we kill and destroy the usb-urb, however, there might still be a pending work-structure which resubmits the now invalid urb. To avoid this race condition, we simply set a shutdown-flag and synchronously kill the worker first. This also adds a comment to all schedule_work()s, as it is really not clear that they are used as replacement for short timers (which can be seen in the git history). Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/bcm203x.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index 8b1b643a519b..ec743c2ddf9d 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -24,6 +24,7 @@ #include +#include #include #include #include @@ -65,6 +66,7 @@ struct bcm203x_data { unsigned long state; struct work_struct work; + atomic_t shutdown; struct urb *urb; unsigned char *buffer; @@ -97,6 +99,7 @@ static void bcm203x_complete(struct urb *urb) data->state = BCM203X_SELECT_MEMORY; + /* use workqueue to have a small delay */ schedule_work(&data->work); break; @@ -155,6 +158,9 @@ static void bcm203x_work(struct work_struct *work) struct bcm203x_data *data = container_of(work, struct bcm203x_data, work); + if (atomic_read(&data->shutdown)) + return; + if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) BT_ERR("Can't submit URB"); } @@ -243,6 +249,7 @@ static int bcm203x_probe(struct usb_interface *intf, const struct usb_device_id usb_set_intfdata(intf, data); + /* use workqueue to have a small delay */ schedule_work(&data->work); return 0; @@ -254,6 +261,9 @@ static void bcm203x_disconnect(struct usb_interface *intf) BT_DBG("intf %p", intf); + atomic_inc(&data->shutdown); + cancel_work_sync(&data->work); + usb_kill_urb(data->urb); usb_set_intfdata(intf, NULL); -- cgit v1.2.3 From deceb024f1083d7eecaba7f2ee65d57f31f91bd5 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 25 Oct 2011 21:13:36 +0200 Subject: Bluetooth: bcm203x: Use GFP_KERNEL in workqueue A workqueue is allowed to sleep so we can safely use GFP_KERNEL instead of GFP_ATOMIC. This is still legacy code when the driver used timer BHs and not a worqueue. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/bcm203x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/bcm203x.c b/drivers/bluetooth/bcm203x.c index ec743c2ddf9d..54952ab800b8 100644 --- a/drivers/bluetooth/bcm203x.c +++ b/drivers/bluetooth/bcm203x.c @@ -161,7 +161,7 @@ static void bcm203x_work(struct work_struct *work) if (atomic_read(&data->shutdown)) return; - if (usb_submit_urb(data->urb, GFP_ATOMIC) < 0) + if (usb_submit_urb(data->urb, GFP_KERNEL) < 0) BT_ERR("Can't submit URB"); } -- cgit v1.2.3 From 7f103a0d23d2778b86cea407c1992522d41ead81 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Wed, 26 Oct 2011 11:22:46 +0200 Subject: Bluetooth: bfusb: Fix error path on firmware load When loading the usb-configuration we do not signal the end of configuration on memory allocation error. This patch moves the memory allocation to the top so every error path uses "goto error" now to correctly send the usb-ctrl message when detecting some error. This also replaces GFP_ATOMIC with GFP_KERNEL as we are allowed to sleep here. Signed-off-by: David Herrmann Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/bfusb.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c index 6580d5057d26..a936763b8c3d 100644 --- a/drivers/bluetooth/bfusb.c +++ b/drivers/bluetooth/bfusb.c @@ -568,22 +568,23 @@ static int bfusb_load_firmware(struct bfusb_data *data, BT_INFO("BlueFRITZ! USB loading firmware"); + buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_KERNEL); + if (!buf) { + BT_ERR("Can't allocate memory chunk for firmware"); + return -ENOMEM; + } + pipe = usb_sndctrlpipe(data->udev, 0); if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION, 0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) { BT_ERR("Can't change to loading configuration"); + kfree(buf); return -EBUSY; } data->udev->toggle[0] = data->udev->toggle[1] = 0; - buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC); - if (!buf) { - BT_ERR("Can't allocate memory chunk for firmware"); - return -ENOMEM; - } - pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep); while (count) { -- cgit v1.2.3 From 25df0845e054f70a8735ee0184739472d8a573c9 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Tue, 1 Nov 2011 17:27:50 +0100 Subject: Bluetooth: hidp: Fix module reference cleanup Calling module_put(THIS_MODULE) is *never* safe when we cannot go sure that we own at least two references. This is because the call may unload our module before it returns and then the "return" will jump into invalid memory. Gladly, module.h provides a wrapper for kthread-users: module_put_and_exit(). This puts our module and then exits the kthread without returning to the module. This patch fixes the hidp kthread to use this wrapper instead of manually freeing its own reference. See nfsd or lockd for other kthreads using this. Calling __module_get() inside the kthread is safe as the hidp module will always wait until the kthread sets "waiting_for_startup" to 0. Signed-off-by: David Herrmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 2efd6cc58b66..56dc660d234a 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -94,7 +94,6 @@ static struct hidp_session *__hidp_get_session(bdaddr_t *bdaddr) static void __hidp_link_session(struct hidp_session *session) { - __module_get(THIS_MODULE); list_add(&session->list, &hidp_session_list); } @@ -103,7 +102,6 @@ static void __hidp_unlink_session(struct hidp_session *session) hci_conn_put_device(session->conn); list_del(&session->list); - module_put(THIS_MODULE); } static void __hidp_copy_session(struct hidp_session *session, struct hidp_conninfo *ci) @@ -703,6 +701,7 @@ static int hidp_session(void *arg) BT_DBG("session %p", session); + __module_get(THIS_MODULE); set_user_nice(current, -15); init_waitqueue_entry(&ctrl_wait, current); @@ -781,6 +780,7 @@ static int hidp_session(void *arg) kfree(session->rd_data); kfree(session); + module_put_and_exit(0); return 0; } -- cgit v1.2.3 From 3c32fa93e5a54cd54e52541892857b0c7164a61e Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Thu, 20 Oct 2011 17:21:34 -0200 Subject: Bluetooth: Fix hidp_get_connection() This functions needs crtl_sock and intr_sock to be set first. Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hidp/core.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index 56dc660d234a..3c2d888925d7 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -996,12 +996,6 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, return -ENOMEM; } - session->conn = hidp_get_connection(session); - if (!session->conn) { - err = -ENOTCONN; - goto failed; - } - bacpy(&session->bdaddr, &bt_sk(ctrl_sock->sk)->dst); session->ctrl_mtu = min_t(uint, l2cap_pi(ctrl_sock->sk)->chan->omtu, @@ -1015,6 +1009,12 @@ int hidp_add_connection(struct hidp_connadd_req *req, struct socket *ctrl_sock, session->intr_sock = intr_sock; session->state = BT_CONNECTED; + session->conn = hidp_get_connection(session); + if (!session->conn) { + err = -ENOTCONN; + goto failed; + } + setup_timer(&session->timer, hidp_idle_timeout, (unsigned long)session); skb_queue_head_init(&session->ctrl_transmit); -- cgit v1.2.3 From 73d80deb7bdf0171f22e76dc2429c1f99eff90e2 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 2 Nov 2011 15:52:01 +0200 Subject: Bluetooth: prioritizing data over HCI This implement priority based scheduler using skbuffer priority set via SO_PRIORITY socket option. It introduces hci_chan_hash (list of HCI Channel/hci_chan) per connection, each item in this list refer to a L2CAP connection and it is used to queue the data for transmission. Signed-off-by: Luiz Augusto von Dentz Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 43 +++++++++++- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/hci_conn.c | 53 +++++++++++++++ net/bluetooth/hci_core.c | 143 ++++++++++++++++++++++++++++++++------- net/bluetooth/l2cap_core.c | 63 +++++++++-------- net/bluetooth/smp.c | 3 +- 6 files changed, 251 insertions(+), 55 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5a9db9a4b439..f97792c972f3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -67,6 +67,12 @@ struct hci_conn_hash { unsigned int le_num; }; +struct hci_chan_hash { + struct list_head list; + spinlock_t lock; + unsigned int num; +}; + struct bdaddr_list { struct list_head list; bdaddr_t bdaddr; @@ -287,6 +293,7 @@ struct hci_conn { unsigned int sent; struct sk_buff_head data_q; + struct hci_chan_hash chan_hash; struct timer_list disc_timer; struct timer_list idle_timer; @@ -309,6 +316,14 @@ struct hci_conn { void (*disconn_cfm_cb) (struct hci_conn *conn, u8 reason); }; +struct hci_chan { + struct list_head list; + + struct hci_conn *conn; + struct sk_buff_head data_q; + unsigned int sent; +}; + extern struct hci_proto *hci_proto[]; extern struct list_head hci_dev_list; extern struct list_head hci_cb_list; @@ -469,6 +484,28 @@ static inline struct hci_conn *hci_conn_hash_lookup_state(struct hci_dev *hdev, return NULL; } +static inline void hci_chan_hash_init(struct hci_conn *c) +{ + struct hci_chan_hash *h = &c->chan_hash; + INIT_LIST_HEAD(&h->list); + spin_lock_init(&h->lock); + h->num = 0; +} + +static inline void hci_chan_hash_add(struct hci_conn *c, struct hci_chan *chan) +{ + struct hci_chan_hash *h = &c->chan_hash; + list_add(&chan->list, &h->list); + h->num++; +} + +static inline void hci_chan_hash_del(struct hci_conn *c, struct hci_chan *chan) +{ + struct hci_chan_hash *h = &c->chan_hash; + list_del(&chan->list); + h->num--; +} + void hci_acl_connect(struct hci_conn *conn); void hci_acl_disconn(struct hci_conn *conn, __u8 reason); void hci_add_sco(struct hci_conn *conn, __u16 handle); @@ -480,6 +517,10 @@ int hci_conn_del(struct hci_conn *conn); void hci_conn_hash_flush(struct hci_dev *hdev); void hci_conn_check_pending(struct hci_dev *hdev); +struct hci_chan *hci_chan_create(struct hci_conn *conn); +int hci_chan_del(struct hci_chan *chan); +void hci_chan_hash_flush(struct hci_conn *conn); + struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type); int hci_conn_check_link_mode(struct hci_conn *conn); @@ -849,7 +890,7 @@ int hci_register_notifier(struct notifier_block *nb); int hci_unregister_notifier(struct notifier_block *nb); int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param); -void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags); +void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags); void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb); void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode); diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index c10bf1db0abb..6ae9492ec564 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -451,6 +451,7 @@ struct l2cap_ops { struct l2cap_conn { struct hci_conn *hcon; + struct hci_chan *hchan; bdaddr_t *dst; bdaddr_t *src; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6e98ff3da2a4..e545376379c5 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -374,6 +374,8 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst) skb_queue_head_init(&conn->data_q); + hci_chan_hash_init(conn); + 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, @@ -432,6 +434,8 @@ int hci_conn_del(struct hci_conn *conn) tasklet_disable(&hdev->tx_task); + hci_chan_hash_flush(conn); + hci_conn_hash_del(hdev, conn); if (hdev->notify) hdev->notify(hdev, HCI_NOTIFY_CONN_DEL); @@ -950,3 +954,52 @@ int hci_get_auth_info(struct hci_dev *hdev, void __user *arg) return copy_to_user(arg, &req, sizeof(req)) ? -EFAULT : 0; } + +struct hci_chan *hci_chan_create(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct hci_chan *chan; + + BT_DBG("%s conn %p", hdev->name, conn); + + chan = kzalloc(sizeof(struct hci_chan), GFP_ATOMIC); + if (!chan) + return NULL; + + chan->conn = conn; + skb_queue_head_init(&chan->data_q); + + tasklet_disable(&hdev->tx_task); + hci_chan_hash_add(conn, chan); + tasklet_enable(&hdev->tx_task); + + return chan; +} + +int hci_chan_del(struct hci_chan *chan) +{ + struct hci_conn *conn = chan->conn; + struct hci_dev *hdev = conn->hdev; + + BT_DBG("%s conn %p chan %p", hdev->name, conn, chan); + + tasklet_disable(&hdev->tx_task); + hci_chan_hash_del(conn, chan); + tasklet_enable(&hdev->tx_task); + + skb_queue_purge(&chan->data_q); + kfree(chan); + + return 0; +} + +void hci_chan_hash_flush(struct hci_conn *conn) +{ + struct hci_chan_hash *h = &conn->chan_hash; + struct hci_chan *chan, *tmp; + + BT_DBG("conn %p", conn); + + list_for_each_entry_safe(chan, tmp, &h->list, list) + hci_chan_del(chan); +} diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f2ec434971f6..631327dc7fed 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1937,23 +1937,18 @@ static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags) hdr->dlen = cpu_to_le16(len); } -void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) +static void hci_queue_acl(struct hci_conn *conn, struct sk_buff_head *queue, + struct sk_buff *skb, __u16 flags) { struct hci_dev *hdev = conn->hdev; struct sk_buff *list; - BT_DBG("%s conn %p flags 0x%x", hdev->name, conn, flags); - - skb->dev = (void *) hdev; - bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; - hci_add_acl_hdr(skb, conn->handle, flags); - list = skb_shinfo(skb)->frag_list; if (!list) { /* Non fragmented */ BT_DBG("%s nonfrag skb %p len %d", hdev->name, skb, skb->len); - skb_queue_tail(&conn->data_q, skb); + skb_queue_tail(queue, skb); } else { /* Fragmented */ BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); @@ -1961,9 +1956,9 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) skb_shinfo(skb)->frag_list = NULL; /* Queue all fragments atomically */ - spin_lock_bh(&conn->data_q.lock); + spin_lock_bh(&queue->lock); - __skb_queue_tail(&conn->data_q, skb); + __skb_queue_tail(queue, skb); flags &= ~ACL_START; flags |= ACL_CONT; @@ -1976,11 +1971,25 @@ void hci_send_acl(struct hci_conn *conn, struct sk_buff *skb, __u16 flags) BT_DBG("%s frag %p len %d", hdev->name, skb, skb->len); - __skb_queue_tail(&conn->data_q, skb); + __skb_queue_tail(queue, skb); } while (list); - spin_unlock_bh(&conn->data_q.lock); + spin_unlock_bh(&queue->lock); } +} + +void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags) +{ + struct hci_conn *conn = chan->conn; + struct hci_dev *hdev = conn->hdev; + + BT_DBG("%s chan %p flags 0x%x", hdev->name, chan, flags); + + skb->dev = (void *) hdev; + bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT; + hci_add_acl_hdr(skb, conn->handle, flags); + + hci_queue_acl(conn, &chan->data_q, skb, flags); tasklet_schedule(&hdev->tx_task); } @@ -2083,11 +2092,90 @@ static inline void hci_link_tx_to(struct hci_dev *hdev, __u8 type) } } -static inline void hci_sched_acl(struct hci_dev *hdev) +static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, + int *quote) { + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_chan *chan = NULL; + int num = 0, min = ~0, cur_prio = 0; struct hci_conn *conn; + int cnt, q, conn_num = 0; + + BT_DBG("%s", hdev->name); + + list_for_each_entry(conn, &h->list, list) { + struct hci_chan_hash *ch; + struct hci_chan *tmp; + + if (conn->type != type) + continue; + + if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) + continue; + + conn_num++; + + ch = &conn->chan_hash; + + list_for_each_entry(tmp, &ch->list, list) { + struct sk_buff *skb; + + if (skb_queue_empty(&tmp->data_q)) + continue; + + skb = skb_peek(&tmp->data_q); + if (skb->priority < cur_prio) + continue; + + if (skb->priority > cur_prio) { + num = 0; + min = ~0; + cur_prio = skb->priority; + } + + num++; + + if (conn->sent < min) { + min = conn->sent; + chan = tmp; + } + } + + if (hci_conn_num(hdev, type) == conn_num) + break; + } + + if (!chan) + return NULL; + + switch (chan->conn->type) { + case ACL_LINK: + cnt = hdev->acl_cnt; + break; + case SCO_LINK: + case ESCO_LINK: + cnt = hdev->sco_cnt; + break; + case LE_LINK: + cnt = hdev->le_mtu ? hdev->le_cnt : hdev->acl_cnt; + break; + default: + cnt = 0; + BT_ERR("Unknown link type"); + } + + q = cnt / num; + *quote = q ? q : 1; + BT_DBG("chan %p quote %d", chan, *quote); + return chan; +} + +static inline void hci_sched_acl(struct hci_dev *hdev) +{ + struct hci_chan *chan; struct sk_buff *skb; int quote; + unsigned int cnt; BT_DBG("%s", hdev->name); @@ -2101,17 +2189,23 @@ static inline void hci_sched_acl(struct hci_dev *hdev) hci_link_tx_to(hdev, ACL_LINK); } - while (hdev->acl_cnt && (conn = hci_low_sent(hdev, ACL_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); + cnt = hdev->acl_cnt; - hci_conn_enter_active_mode(conn, bt_cb(skb)->force_active); + while (hdev->acl_cnt && + (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { + while (quote-- && (skb = skb_dequeue(&chan->data_q))) { + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, + skb->len, skb->priority); + + hci_conn_enter_active_mode(chan->conn, + bt_cb(skb)->force_active); hci_send_frame(skb); hdev->acl_last_tx = jiffies; hdev->acl_cnt--; - conn->sent++; + chan->sent++; + chan->conn->sent++; } } } @@ -2165,7 +2259,7 @@ static inline void hci_sched_esco(struct hci_dev *hdev) static inline void hci_sched_le(struct hci_dev *hdev) { - struct hci_conn *conn; + struct hci_chan *chan; struct sk_buff *skb; int quote, cnt; @@ -2183,17 +2277,20 @@ static inline void hci_sched_le(struct hci_dev *hdev) } cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; - while (cnt && (conn = hci_low_sent(hdev, LE_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&conn->data_q))) { - BT_DBG("skb %p len %d", skb, skb->len); + while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { + while (quote-- && (skb = skb_dequeue(&chan->data_q))) { + BT_DBG("chan %p skb %p len %d priority %u", chan, skb, + skb->len, skb->priority); hci_send_frame(skb); hdev->le_last_tx = jiffies; cnt--; - conn->sent++; + chan->sent++; + chan->conn->sent++; } } + if (hdev->le_pkts) hdev->le_cnt = cnt; else diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ac2c41ada0fe..15751fa5e914 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -566,7 +566,25 @@ static void l2cap_send_cmd(struct l2cap_conn *conn, u8 ident, u8 code, u16 len, bt_cb(skb)->force_active = BT_POWER_FORCE_ACTIVE_ON; skb->priority = HCI_PRIO_MAX; - hci_send_acl(conn->hcon, skb, flags); + hci_send_acl(conn->hchan, skb, flags); +} + +static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) +{ + struct hci_conn *hcon = chan->conn->hcon; + u16 flags; + + BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, + skb->priority); + + if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && + lmp_no_flush_capable(hcon->hdev)) + flags = ACL_START_NO_FLUSH; + else + flags = ACL_START; + + bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); + hci_send_acl(chan->conn->hchan, skb, flags); } static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) @@ -575,7 +593,6 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) struct l2cap_hdr *lh; struct l2cap_conn *conn = chan->conn; int count, hlen; - u8 flags; if (chan->state != BT_CONNECTED) return; @@ -615,14 +632,8 @@ static inline void l2cap_send_sframe(struct l2cap_chan *chan, u32 control) put_unaligned_le16(fcs, skb_put(skb, L2CAP_FCS_SIZE)); } - if (lmp_no_flush_capable(conn->hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); - - hci_send_acl(chan->conn->hcon, skb, flags); + skb->priority = HCI_PRIO_MAX; + l2cap_do_send(chan, skb); } static inline void l2cap_send_rr_or_rnr(struct l2cap_chan *chan, u32 control) @@ -1002,6 +1013,8 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) chan->ops->close(chan->data); } + hci_chan_del(conn->hchan); + if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) del_timer_sync(&conn->info_timer); @@ -1024,18 +1037,26 @@ static void security_timeout(unsigned long arg) static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) { struct l2cap_conn *conn = hcon->l2cap_data; + struct hci_chan *hchan; if (conn || status) return conn; + hchan = hci_chan_create(hcon); + if (!hchan) + return NULL; + conn = kzalloc(sizeof(struct l2cap_conn), GFP_ATOMIC); - if (!conn) + if (!conn) { + hci_chan_del(hchan); return NULL; + } hcon->l2cap_data = conn; conn->hcon = hcon; + conn->hchan = hchan; - BT_DBG("hcon %p conn %p", hcon, conn); + BT_DBG("hcon %p conn %p hchan %p", hcon, conn, hchan); if (hcon->hdev->le_mtu && hcon->type == LE_LINK) conn->mtu = hcon->hdev->le_mtu; @@ -1261,24 +1282,6 @@ static void l2cap_drop_acked_frames(struct l2cap_chan *chan) __clear_retrans_timer(chan); } -static void l2cap_do_send(struct l2cap_chan *chan, struct sk_buff *skb) -{ - struct hci_conn *hcon = chan->conn->hcon; - u16 flags; - - BT_DBG("chan %p, skb %p len %d priority %u", chan, skb, skb->len, - skb->priority); - - if (!test_bit(FLAG_FLUSHABLE, &chan->flags) && - lmp_no_flush_capable(hcon->hdev)) - flags = ACL_START_NO_FLUSH; - else - flags = ACL_START; - - bt_cb(skb)->force_active = test_bit(FLAG_FORCE_ACTIVE, &chan->flags); - hci_send_acl(hcon, skb, flags); -} - static void l2cap_streaming_send(struct l2cap_chan *chan) { struct sk_buff *skb; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 759b63572641..94e94ca35384 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -181,7 +181,8 @@ static void smp_send_cmd(struct l2cap_conn *conn, u8 code, u16 len, void *data) if (!skb) return; - hci_send_acl(conn->hcon, skb, 0); + skb->priority = HCI_PRIO_MAX; + hci_send_acl(conn->hchan, skb, 0); mod_timer(&conn->security_timer, jiffies + msecs_to_jiffies(SMP_TIMEOUT)); -- cgit v1.2.3 From ec1cce24d5950e797f10650abf7890ead67c6e64 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 2 Nov 2011 15:52:02 +0200 Subject: Bluetooth: handle priority change within quote The quote is calculated based on the first buffer in the queue so if the priority changes to something lower than the priority of the first skb the quote needs to be recalculated. Signed-off-by: Luiz Augusto von Dentz Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 631327dc7fed..19e44533fb01 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2193,10 +2193,17 @@ static inline void hci_sched_acl(struct hci_dev *hdev) while (hdev->acl_cnt && (chan = hci_chan_sent(hdev, ACL_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&chan->data_q))) { + u32 priority = (skb_peek(&chan->data_q))->priority; + while (quote-- && (skb = skb_peek(&chan->data_q))) { BT_DBG("chan %p skb %p len %d priority %u", chan, skb, skb->len, skb->priority); + /* Stop if priority has changed */ + if (skb->priority < priority) + break; + + skb = skb_dequeue(&chan->data_q); + hci_conn_enter_active_mode(chan->conn, bt_cb(skb)->force_active); @@ -2278,10 +2285,17 @@ static inline void hci_sched_le(struct hci_dev *hdev) cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { - while (quote-- && (skb = skb_dequeue(&chan->data_q))) { + u32 priority = (skb_peek(&chan->data_q))->priority; + while (quote-- && (skb = skb_peek(&chan->data_q))) { BT_DBG("chan %p skb %p len %d priority %u", chan, skb, skb->len, skb->priority); + /* Stop if priority has changed */ + if (skb->priority < priority) + break; + + skb = skb_dequeue(&chan->data_q); + hci_send_frame(skb); hdev->le_last_tx = jiffies; -- cgit v1.2.3 From 02b20f0bb661829cbd431e5deb2474e909e65cec Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 2 Nov 2011 15:52:03 +0200 Subject: Bluetooth: recalculate priorities when channels are starving To avoid starvation the priority is recalculated so that the starving channels are promoted to HCI_PRIO_MAX - 1 (6). HCI_PRIO_MAX (7) is considered special, because it requires CAP_NET_ADMIN capability which can be used to provide more guaranties, so it is not used when promoting. Signed-off-by: Luiz Augusto von Dentz Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 19e44533fb01..4221fd5b1f4b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2170,6 +2170,53 @@ static inline struct hci_chan *hci_chan_sent(struct hci_dev *hdev, __u8 type, return chan; } +static void hci_prio_recalculate(struct hci_dev *hdev, __u8 type) +{ + struct hci_conn_hash *h = &hdev->conn_hash; + struct hci_conn *conn; + int num = 0; + + BT_DBG("%s", hdev->name); + + list_for_each_entry(conn, &h->list, list) { + struct hci_chan_hash *ch; + struct hci_chan *chan; + + if (conn->type != type) + continue; + + if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG) + continue; + + num++; + + ch = &conn->chan_hash; + list_for_each_entry(chan, &ch->list, list) { + struct sk_buff *skb; + + if (chan->sent) { + chan->sent = 0; + continue; + } + + if (skb_queue_empty(&chan->data_q)) + continue; + + skb = skb_peek(&chan->data_q); + if (skb->priority >= HCI_PRIO_MAX - 1) + continue; + + skb->priority = HCI_PRIO_MAX - 1; + + BT_DBG("chan %p skb %p promoted to %d", chan, skb, + skb->priority); + } + + if (hci_conn_num(hdev, type) == num) + break; + } +} + static inline void hci_sched_acl(struct hci_dev *hdev) { struct hci_chan *chan; @@ -2215,6 +2262,9 @@ static inline void hci_sched_acl(struct hci_dev *hdev) chan->conn->sent++; } } + + if (cnt != hdev->acl_cnt) + hci_prio_recalculate(hdev, ACL_LINK); } /* Schedule SCO */ @@ -2268,7 +2318,7 @@ static inline void hci_sched_le(struct hci_dev *hdev) { struct hci_chan *chan; struct sk_buff *skb; - int quote, cnt; + int quote, cnt, tmp; BT_DBG("%s", hdev->name); @@ -2284,6 +2334,7 @@ static inline void hci_sched_le(struct hci_dev *hdev) } cnt = hdev->le_pkts ? hdev->le_cnt : hdev->acl_cnt; + tmp = cnt; while (cnt && (chan = hci_chan_sent(hdev, LE_LINK, "e))) { u32 priority = (skb_peek(&chan->data_q))->priority; while (quote-- && (skb = skb_peek(&chan->data_q))) { @@ -2309,6 +2360,9 @@ static inline void hci_sched_le(struct hci_dev *hdev) hdev->le_cnt = cnt; else hdev->acl_cnt = cnt; + + if (cnt != tmp) + hci_prio_recalculate(hdev, LE_LINK); } static void hci_tx_task(unsigned long arg) -- cgit v1.2.3 From b8aabfc92249b239c425da7e4ca85b7e4855e984 Mon Sep 17 00:00:00 2001 From: Luiz Augusto von Dentz Date: Wed, 2 Nov 2011 15:52:04 +0200 Subject: Bluetooth: use buffer priority to mark URB_ISO_ASAP flag Signed-off-by: Luiz Augusto von Dentz Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- drivers/bluetooth/btusb.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index abfc4ee87ee8..9db247631238 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -727,6 +727,9 @@ static int btusb_send_frame(struct sk_buff *skb) usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len, btusb_tx_complete, skb); + if (skb->priority >= HCI_PRIO_MAX - 1) + urb->transfer_flags = URB_ISO_ASAP; + hdev->stat.acl_tx++; break; -- cgit v1.2.3 From 6b3c7104677a731cf6d3638e09d9d6c530b9bc25 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Wed, 2 Nov 2011 09:57:10 +0200 Subject: Bluetooth: Initialize tx_win_max for fixed channel tx_win_max is initialized during L2CAP configuration phase. For fixed channels (e.g. A2MP) we want to have it initialized when channel is created. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_sock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 646aefc4f1d7..9ed6501d90f6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -943,6 +943,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->fcs = pchan->fcs; chan->max_tx = pchan->max_tx; chan->tx_win = pchan->tx_win; + chan->tx_win_max = pchan->tx_win_max; chan->sec_level = pchan->sec_level; chan->flags = pchan->flags; } else { @@ -971,6 +972,7 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent) chan->max_tx = L2CAP_DEFAULT_MAX_TX; chan->fcs = L2CAP_FCS_CRC16; chan->tx_win = L2CAP_DEFAULT_TX_WINDOW; + chan->tx_win_max = L2CAP_DEFAULT_TX_WINDOW; chan->sec_level = BT_SECURITY_LOW; chan->flags = 0; set_bit(FLAG_FORCE_ACTIVE, &chan->flags); -- cgit v1.2.3 From c14968b0c1792901ac1cbbbf18f42e37b5a6f4df Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:28 -0700 Subject: Bluetooth: Add BT_CHANNEL_POLICY socket option Allow control of AMP functionality on L2CAP sockets. By default, connections will be restricted to BR/EDR. Manipulating the BT_CHANNEL_POLICY option allows for channels to be moved to or created on AMP controllers. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/bluetooth.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index fb1acb3454ae..38cd3dab7f1d 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -77,6 +77,33 @@ struct bt_power { #define BT_POWER_FORCE_ACTIVE_OFF 0 #define BT_POWER_FORCE_ACTIVE_ON 1 +#define BT_CHANNEL_POLICY 10 + +/* BR/EDR only (default policy) + * AMP controllers cannot be used. + * Channel move requests from the remote device are denied. + * If the L2CAP channel is currently using AMP, move the channel to BR/EDR. + */ +#define BT_CHANNEL_POLICY_BREDR_ONLY 0 + +/* BR/EDR Preferred + * Allow use of AMP controllers. + * If the L2CAP channel is currently on AMP, move it to BR/EDR. + * Channel move requests from the remote device are allowed. + */ +#define BT_CHANNEL_POLICY_BREDR_PREFERRED 1 + +/* AMP Preferred + * Allow use of AMP controllers + * If the L2CAP channel is currently on BR/EDR and AMP controller + * resources are available, initiate a channel move to AMP. + * Channel move requests from the remote device are allowed. + * If the L2CAP socket has not been connected yet, try to create + * and configure the channel directly on an AMP controller rather + * than BR/EDR. + */ +#define BT_CHANNEL_POLICY_AMP_PREFERRED 2 + __attribute__((format (printf, 2, 3))) int bt_printk(const char *level, const char *fmt, ...); -- cgit v1.2.3 From d7c4d11c649ae694b78f145120ed693a004fe496 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:29 -0700 Subject: Bluetooth: Change scope of the enable_hs module parameter This variable is currently only accessible within l2cap_core.c, but it is also needed in l2cap_sock.c Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 6ae9492ec564..1a62573dbd67 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -734,6 +734,7 @@ static inline __u8 __ctrl_size(struct l2cap_chan *chan) } extern int disable_ertm; +extern int enable_hs; int l2cap_init_sockets(void); void l2cap_cleanup_sockets(void); -- cgit v1.2.3 From 2ea664822af6705574dfbbf8c77fc7d75a94e9b3 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:30 -0700 Subject: Bluetooth: Add channel policy to getsockopt/setsockopt Each channel has a policy to require BR/EDR (the default), prefer BR/EDR, or prefer AMP. Check for valid policy value and L2CAP mode. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/l2cap_sock.c | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1a62573dbd67..9c7d06e6b987 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -367,6 +367,7 @@ struct l2cap_chan { __u16 flush_to; __u8 mode; __u8 chan_type; + __u8 chan_policy; __le16 sport; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 9ed6501d90f6..664762e89ecf 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -467,6 +467,16 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch break; + case BT_CHANNEL_POLICY: + if (!enable_hs) { + err = -ENOPROTOOPT; + break; + } + + if (put_user(chan->chan_policy, (u32 __user *) optval)) + err = -EFAULT; + break; + default: err = -ENOPROTOOPT; break; @@ -690,6 +700,31 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch clear_bit(FLAG_FORCE_ACTIVE, &chan->flags); break; + case BT_CHANNEL_POLICY: + if (!enable_hs) { + err = -ENOPROTOOPT; + break; + } + + if (get_user(opt, (u32 __user *) optval)) { + err = -EFAULT; + break; + } + + if (opt > BT_CHANNEL_POLICY_AMP_PREFERRED) { + err = -EINVAL; + break; + } + + if (chan->mode != L2CAP_MODE_ERTM && + chan->mode != L2CAP_MODE_STREAMING) { + err = -EOPNOTSUPP; + break; + } + + chan->chan_policy = (u8) opt; + break; + default: err = -ENOPROTOOPT; break; -- cgit v1.2.3 From 38094c75b54c52b45f48b80fd2f6d1138a1b9b2b Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:31 -0700 Subject: Bluetooth: Add AMP-related data and structures for channel signals AMP channel creation and channel moves are coordinated using the L2CAP signaling channel. These definitions cover the "create channel", "move channel", and "move channel confirm" signals. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 54 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 9c7d06e6b987..88d462a4ee78 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -94,6 +94,12 @@ struct l2cap_conninfo { #define L2CAP_ECHO_RSP 0x09 #define L2CAP_INFO_REQ 0x0a #define L2CAP_INFO_RSP 0x0b +#define L2CAP_CREATE_CHAN_REQ 0x0c +#define L2CAP_CREATE_CHAN_RSP 0x0d +#define L2CAP_MOVE_CHAN_REQ 0x0e +#define L2CAP_MOVE_CHAN_RSP 0x0f +#define L2CAP_MOVE_CHAN_CFM 0x10 +#define L2CAP_MOVE_CHAN_CFM_RSP 0x11 #define L2CAP_CONN_PARAM_UPDATE_REQ 0x12 #define L2CAP_CONN_PARAM_UPDATE_RSP 0x13 @@ -217,14 +223,15 @@ struct l2cap_conn_rsp { #define L2CAP_CID_DYN_START 0x0040 #define L2CAP_CID_DYN_END 0xffff -/* connect result */ +/* connect/create channel results */ #define L2CAP_CR_SUCCESS 0x0000 #define L2CAP_CR_PEND 0x0001 #define L2CAP_CR_BAD_PSM 0x0002 #define L2CAP_CR_SEC_BLOCK 0x0003 #define L2CAP_CR_NO_MEM 0x0004 +#define L2CAP_CR_BAD_AMP 0x0005 -/* connect status */ +/* connect/create channel status */ #define L2CAP_CS_NO_INFO 0x0000 #define L2CAP_CS_AUTHEN_PEND 0x0001 #define L2CAP_CS_AUTHOR_PEND 0x0002 @@ -319,6 +326,49 @@ struct l2cap_info_rsp { __u8 data[0]; } __packed; +struct l2cap_create_chan_req { + __le16 psm; + __le16 scid; + __u8 amp_id; +} __packed; + +struct l2cap_create_chan_rsp { + __le16 dcid; + __le16 scid; + __le16 result; + __le16 status; +} __packed; + +struct l2cap_move_chan_req { + __le16 icid; + __u8 dest_amp_id; +} __packed; + +struct l2cap_move_chan_rsp { + __le16 icid; + __le16 result; +} __packed; + +#define L2CAP_MR_SUCCESS 0x0000 +#define L2CAP_MR_PEND 0x0001 +#define L2CAP_MR_BAD_ID 0x0002 +#define L2CAP_MR_SAME_ID 0x0003 +#define L2CAP_MR_NOT_SUPP 0x0004 +#define L2CAP_MR_COLLISION 0x0005 +#define L2CAP_MR_NOT_ALLOWED 0x0006 + +struct l2cap_move_chan_cfm { + __le16 icid; + __le16 result; +} __packed; + +#define L2CAP_MC_CONFIRMED 0x0000 +#define L2CAP_MC_UNCONFIRMED 0x0001 + +struct l2cap_move_chan_cfm_rsp { + __le16 icid; +} __packed; + /* info type */ #define L2CAP_IT_CL_MTU 0x0001 #define L2CAP_IT_FEAT_MASK 0x0002 -- cgit v1.2.3 From f94ff6fff7b8b5896a173d165e9ec579c83067f2 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:32 -0700 Subject: Bluetooth: Add signal handlers for channel creation Handle both "create channel request" and "create channel response". Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 47 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 15751fa5e914..cf48330a7fdc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3104,6 +3104,45 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm return 0; } +static inline int l2cap_create_channel_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, + void *data) +{ + struct l2cap_create_chan_req *req = data; + struct l2cap_create_chan_rsp rsp; + u16 psm, scid; + + if (cmd_len != sizeof(*req)) + return -EPROTO; + + if (!enable_hs) + return -EINVAL; + + psm = le16_to_cpu(req->psm); + scid = le16_to_cpu(req->scid); + + BT_DBG("psm %d, scid %d, amp_id %d", psm, scid, req->amp_id); + + /* Placeholder: Always reject */ + rsp.dcid = 0; + rsp.scid = cpu_to_le16(scid); + rsp.result = L2CAP_CR_NO_MEM; + rsp.status = L2CAP_CS_NO_INFO; + + l2cap_send_cmd(conn, cmd->ident, L2CAP_CREATE_CHAN_RSP, + sizeof(rsp), &rsp); + + return 0; +} + +static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, void *data) +{ + BT_DBG("conn %p", conn); + + return l2cap_connect_rsp(conn, cmd, data); +} + static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, u16 to_multiplier) { @@ -3216,6 +3255,14 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, err = l2cap_information_rsp(conn, cmd, data); break; + case L2CAP_CREATE_CHAN_REQ: + err = l2cap_create_channel_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_CREATE_CHAN_RSP: + err = l2cap_create_channel_rsp(conn, cmd, data); + break; + default: BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); err = -EINVAL; -- cgit v1.2.3 From d835ac0fc73276893af63a478317027787a3ac1f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:33 -0700 Subject: Bluetooth: Add definitions for L2CAP fixed channels Symbolic fixed channel IDs will be used instead of magic numbers. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/l2cap.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 88d462a4ee78..9280bff55430 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -119,6 +119,10 @@ struct l2cap_conninfo { #define L2CAP_FCS_NONE 0x00 #define L2CAP_FCS_CRC16 0x01 +/* L2CAP fixed channels */ +#define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_A2MP 0x08 + /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 #define L2CAP_CTRL_REQSEQ 0x3F00 -- cgit v1.2.3 From 50a147cd9c7523d1b11d0284a0be7891631517dd Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:34 -0700 Subject: Bluetooth: Use symbolic values for the fixed channel map The A2MP fixed channel bit is only set when high-speed mode is enabled. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index cf48330a7fdc..0272cd1519fa 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -60,7 +60,7 @@ int disable_ertm; int enable_hs; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN; -static u8 l2cap_fixed_chan[8] = { 0x02, }; +static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); @@ -3035,6 +3035,12 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm } else if (type == L2CAP_IT_FIXED_CHAN) { u8 buf[12]; struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf; + + if (enable_hs) + l2cap_fixed_chan[0] |= L2CAP_FC_A2MP; + else + l2cap_fixed_chan[0] &= ~L2CAP_FC_A2MP; + rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN); rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS); memcpy(rsp->data, l2cap_fixed_chan, sizeof(l2cap_fixed_chan)); -- cgit v1.2.3 From 8d5a04a130e3493c17eae7a881102ab1a4283736 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:35 -0700 Subject: Bluetooth: Add signal handlers for channel moves AMP channels can be moved between BR/EDR and AMP controllers using a sequence of signals. Every attempted channel move involves a series of four signals: Move Initiator Move Responder | | | Move Channel Request | | ----------------------------> | | | | Move Channel Response | | <---------------------------- | | | | Move Channel Confirm | | ----------------------------> | | | | Move Channel Confirm Response | | <---------------------------- | All four signals are sent even if the move fails. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 136 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 136 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0272cd1519fa..335dc6fbccf7 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3149,6 +3149,126 @@ static inline int l2cap_create_channel_rsp(struct l2cap_conn *conn, return l2cap_connect_rsp(conn, cmd, data); } +static void l2cap_send_move_chan_rsp(struct l2cap_conn *conn, u8 ident, + u16 icid, u16 result) +{ + struct l2cap_move_chan_rsp rsp; + + BT_DBG("icid %d, result %d", icid, result); + + rsp.icid = cpu_to_le16(icid); + rsp.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_RSP, sizeof(rsp), &rsp); +} + +static void l2cap_send_move_chan_cfm(struct l2cap_conn *conn, + struct l2cap_chan *chan, u16 icid, u16 result) +{ + struct l2cap_move_chan_cfm cfm; + u8 ident; + + BT_DBG("icid %d, result %d", icid, result); + + ident = l2cap_get_ident(conn); + if (chan) + chan->ident = ident; + + cfm.icid = cpu_to_le16(icid); + cfm.result = cpu_to_le16(result); + + l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM, sizeof(cfm), &cfm); +} + +static void l2cap_send_move_chan_cfm_rsp(struct l2cap_conn *conn, u8 ident, + u16 icid) +{ + struct l2cap_move_chan_cfm_rsp rsp; + + BT_DBG("icid %d", icid); + + rsp.icid = cpu_to_le16(icid); + l2cap_send_cmd(conn, ident, L2CAP_MOVE_CHAN_CFM_RSP, sizeof(rsp), &rsp); +} + +static inline int l2cap_move_channel_req(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_req *req = data; + u16 icid = 0; + u16 result = L2CAP_MR_NOT_ALLOWED; + + if (cmd_len != sizeof(*req)) + return -EPROTO; + + icid = le16_to_cpu(req->icid); + + BT_DBG("icid %d, dest_amp_id %d", icid, req->dest_amp_id); + + if (!enable_hs) + return -EINVAL; + + /* Placeholder: Always refuse */ + l2cap_send_move_chan_rsp(conn, cmd->ident, icid, result); + + return 0; +} + +static inline int l2cap_move_channel_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_rsp *rsp = data; + u16 icid, result; + + if (cmd_len != sizeof(*rsp)) + return -EPROTO; + + icid = le16_to_cpu(rsp->icid); + result = le16_to_cpu(rsp->result); + + BT_DBG("icid %d, result %d", icid, result); + + /* Placeholder: Always unconfirmed */ + l2cap_send_move_chan_cfm(conn, NULL, icid, L2CAP_MC_UNCONFIRMED); + + return 0; +} + +static inline int l2cap_move_channel_confirm(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_cfm *cfm = data; + u16 icid, result; + + if (cmd_len != sizeof(*cfm)) + return -EPROTO; + + icid = le16_to_cpu(cfm->icid); + result = le16_to_cpu(cfm->result); + + BT_DBG("icid %d, result %d", icid, result); + + l2cap_send_move_chan_cfm_rsp(conn, cmd->ident, icid); + + return 0; +} + +static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, + struct l2cap_cmd_hdr *cmd, u16 cmd_len, void *data) +{ + struct l2cap_move_chan_cfm_rsp *rsp = data; + u16 icid; + + if (cmd_len != sizeof(*rsp)) + return -EPROTO; + + icid = le16_to_cpu(rsp->icid); + + BT_DBG("icid %d", icid); + + return 0; +} + static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, u16 to_multiplier) { @@ -3269,6 +3389,22 @@ static inline int l2cap_bredr_sig_cmd(struct l2cap_conn *conn, err = l2cap_create_channel_rsp(conn, cmd, data); break; + case L2CAP_MOVE_CHAN_REQ: + err = l2cap_move_channel_req(conn, cmd, cmd_len, data); + break; + + case L2CAP_MOVE_CHAN_RSP: + err = l2cap_move_channel_rsp(conn, cmd, cmd_len, data); + break; + + case L2CAP_MOVE_CHAN_CFM: + err = l2cap_move_channel_confirm(conn, cmd, cmd_len, data); + break; + + case L2CAP_MOVE_CHAN_CFM_RSP: + err = l2cap_move_channel_confirm_rsp(conn, cmd, cmd_len, data); + break; + default: BT_ERR("Unknown BR/EDR signaling command 0x%2.2x", cmd->code); err = -EINVAL; -- cgit v1.2.3 From 08add513caa8930b8f7b9d5837a7dda624741745 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 2 Nov 2011 16:18:36 -0700 Subject: Bluetooth: Guarantee BR-EDR device will be registered as hci0 It's convenient to use the HCI device index the AMP controller id, but the spec requires that an AMP controller never has id 0. Signed-off-by: Mat Martineau Acked-by: Marcel Holtmann Acked-by: Andrei Emeltchenko Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4221fd5b1f4b..b7f6b5bc1bb4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1410,7 +1410,7 @@ int hci_add_adv_entry(struct hci_dev *hdev, int hci_register_dev(struct hci_dev *hdev) { struct list_head *head = &hci_dev_list, *p; - int i, id = 0, error; + int i, id, error; BT_DBG("%p name %s bus %d owner %p", hdev, hdev->name, hdev->bus, hdev->owner); @@ -1418,6 +1418,11 @@ int hci_register_dev(struct hci_dev *hdev) if (!hdev->open || !hdev->close || !hdev->destruct) return -EINVAL; + /* Do not allow HCI_AMP devices to register at index 0, + * so the index can be used as the AMP controller ID. + */ + id = (hdev->dev_type == HCI_BREDR) ? 0 : 1; + write_lock_bh(&hci_dev_list_lock); /* Find first available device id */ -- cgit v1.2.3 From b24752fe655e9427240a5fe840914b94e5f9c2bc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Nov 2011 14:40:33 +0200 Subject: Bluetooth: Fix mgmt response when adapter goes down or is removed When an adapter gets powered off or is removed any pending commands should receive a ENETDOWN or ENODEV status response. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index cbc8a6dfa5ed..747366a1f23c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -262,7 +262,7 @@ static void mgmt_pending_foreach(u16 opcode, int index, cmd = list_entry(p, struct pending_cmd, list); - if (cmd->opcode != opcode) + if (opcode > 0 && cmd->opcode != opcode) continue; if (index >= 0 && cmd->index != index) @@ -1949,6 +1949,14 @@ done: return err; } +static void cmd_status_rsp(struct pending_cmd *cmd, void *data) +{ + u8 *status = data; + + cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); + mgmt_pending_remove(cmd); +} + int mgmt_index_added(u16 index) { return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL); @@ -1956,6 +1964,10 @@ int mgmt_index_added(u16 index) int mgmt_index_removed(u16 index) { + u8 status = ENODEV; + + mgmt_pending_foreach(0, index, cmd_status_rsp, &status); + return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); } @@ -1992,6 +2004,11 @@ int mgmt_powered(u16 index, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); + if (!powered) { + u8 status = ENETDOWN; + mgmt_pending_foreach(0, index, cmd_status_rsp, &status); + } + ev.val = powered; ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); -- cgit v1.2.3 From abc545b8efe3d50d649590df4b88cc652fd057f1 Mon Sep 17 00:00:00 2001 From: Szymon Janc Date: Thu, 3 Nov 2011 16:05:44 +0100 Subject: Bluetooth: Add debug print to l2cap_chan_create Signed-off-by: Szymon Janc Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 335dc6fbccf7..fe5666e15298 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -294,6 +294,8 @@ struct l2cap_chan *l2cap_chan_create(struct sock *sk) atomic_set(&chan->refcnt, 1); + BT_DBG("sk %p chan %p", sk, chan); + return chan; } -- cgit v1.2.3 From 36f7fc7e9ac72507ab7bf6caf77ad252c12ab37e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Nov 2011 00:17:45 +0200 Subject: Bluetooth: Clean up logic in hci_cc_write_scan_enable This patch adds a new label to hci_cc_write_scan_enable to avoid unnecessary indentation. This will be convenient especially when new code for the discoverable timeout gets added. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d8fa65709aed..8c81a75381fb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -274,7 +274,8 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { - __u8 status = *((__u8 *) skb->data); + __u8 param, status = *((__u8 *) skb->data); + int old_pscan, old_iscan; void *sent; BT_DBG("%s status 0x%x", hdev->name, status); @@ -283,28 +284,29 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; - if (!status) { - __u8 param = *((__u8 *) sent); - int old_pscan, old_iscan; + if (status != 0) + goto done; - old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); - old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); + param = *((__u8 *) sent); - if (param & SCAN_INQUIRY) { - set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev->id, 1); - } else if (old_iscan) - mgmt_discoverable(hdev->id, 0); + old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); + old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); - if (param & SCAN_PAGE) { - set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev->id, 1); - } else if (old_pscan) - mgmt_connectable(hdev->id, 0); - } + if (param & SCAN_INQUIRY) { + set_bit(HCI_ISCAN, &hdev->flags); + if (!old_iscan) + mgmt_discoverable(hdev->id, 1); + } else if (old_iscan) + mgmt_discoverable(hdev->id, 0); + + if (param & SCAN_PAGE) { + set_bit(HCI_PSCAN, &hdev->flags); + if (!old_pscan) + mgmt_connectable(hdev->id, 1); + } else if (old_pscan) + mgmt_connectable(hdev->id, 0); +done: hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); } -- cgit v1.2.3 From 9f5a0d7bf079e9e26771ad13ff1c2cb3adf80963 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 7 Nov 2011 14:20:25 +0200 Subject: Bluetooth: Define HCI reasons instead of magic number Use HCI error reasons instead of magic numbers. Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci.h | 7 +++++++ include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_conn.c | 2 +- net/bluetooth/hci_event.c | 4 ++-- net/bluetooth/l2cap_core.c | 8 ++++---- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index c5fcd13b9edf..139ce2aa6eee 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -264,6 +264,13 @@ enum { #define HCI_LK_SMP_IRK 0x82 #define HCI_LK_SMP_CSRK 0x83 +/* ---- HCI Error Codes ---- */ +#define HCI_ERROR_AUTH_FAILURE 0x05 +#define HCI_ERROR_REJ_BAD_ADDR 0x0f +#define HCI_ERROR_REMOTE_USER_TERM 0x13 +#define HCI_ERROR_LOCAL_HOST_TERM 0x16 +#define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 + /* ----- HCI Commands ---- */ #define HCI_OP_NOP 0x0000 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f97792c972f3..006a7699abac 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -730,7 +730,7 @@ 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 = 0x13; + int reason = HCI_ERROR_REMOTE_USER_TERM; hp = hci_proto[HCI_PROTO_L2CAP]; if (hp && hp->disconn_ind) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e545376379c5..ac943676f78d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -820,7 +820,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev) c->state = BT_CLOSED; - hci_proto_disconn_cfm(c, 0x16); + hci_proto_disconn_cfm(c, HCI_ERROR_LOCAL_HOST_TERM); hci_conn_del(c); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8c81a75381fb..9dc54db693a3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1559,7 +1559,7 @@ static inline void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *sk struct hci_cp_reject_conn_req cp; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = 0x0f; + cp.reason = HCI_ERROR_REJ_BAD_ADDR; hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); } } @@ -2646,7 +2646,7 @@ static inline void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff struct hci_cp_io_capability_neg_reply cp; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = 0x18; /* Pairing not allowed */ + cp.reason = HCI_ERROR_PAIRING_NOT_ALLOWED; hci_send_cmd(hdev, HCI_OP_IO_CAPABILITY_NEG_REPLY, sizeof(cp), &cp); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index fe5666e15298..a50610b8a00c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -313,7 +313,7 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct l2cap_chan *chan) BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, chan->psm, chan->dcid); - conn->disc_reason = 0x13; + conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; chan->conn = conn; @@ -1082,7 +1082,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status) setup_timer(&conn->info_timer, l2cap_info_timeout, (unsigned long) conn); - conn->disc_reason = 0x13; + conn->disc_reason = HCI_ERROR_REMOTE_USER_TERM; return conn; } @@ -2535,7 +2535,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd /* Check if the ACL is secure enough (if not SDP) */ if (psm != cpu_to_le16(0x0001) && !hci_conn_check_link_mode(conn->hcon)) { - conn->disc_reason = 0x05; + conn->disc_reason = HCI_ERROR_AUTH_FAILURE; result = L2CAP_CR_SEC_BLOCK; goto response; } @@ -4411,7 +4411,7 @@ static int l2cap_disconn_ind(struct hci_conn *hcon) BT_DBG("hcon %p", hcon); if ((hcon->type != ACL_LINK && hcon->type != LE_LINK) || !conn) - return 0x13; + return HCI_ERROR_REMOTE_USER_TERM; return conn->disc_reason; } -- cgit v1.2.3 From 66af7aaf9edff55b7995bbe1ff508513666d0671 Mon Sep 17 00:00:00 2001 From: Andrei Emeltchenko Date: Mon, 7 Nov 2011 14:20:33 +0200 Subject: Bluetooth: EFS: parse L2CAP config response Add parsing Extended Flow Specification in L2CAP Config Response. Based upon haijun.liu series of patches (sent Sun, 22 Aug 2010) Signed-off-by: Andrei Emeltchenko Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/l2cap_core.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a50610b8a00c..f8506849b67e 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2351,6 +2351,7 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi int type, olen; unsigned long val; struct l2cap_conf_rfc rfc; + struct l2cap_conf_efs efs; BT_DBG("chan %p, rsp %p, len %d, req %p", chan, rsp, len, data); @@ -2393,6 +2394,19 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi l2cap_add_conf_opt(&ptr, L2CAP_CONF_EWS, 2, chan->tx_win); break; + + case L2CAP_CONF_EFS: + if (olen == sizeof(efs)) + memcpy(&efs, (void *)val, olen); + + if (chan->local_stype != L2CAP_SERV_NOTRAFIC && + efs.stype != L2CAP_SERV_NOTRAFIC && + efs.stype != chan->local_stype) + return -ECONNREFUSED; + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_EFS, + sizeof(efs), (unsigned long) &efs); + break; } } @@ -2407,7 +2421,17 @@ static int l2cap_parse_conf_rsp(struct l2cap_chan *chan, void *rsp, int len, voi chan->retrans_timeout = le16_to_cpu(rfc.retrans_timeout); chan->monitor_timeout = le16_to_cpu(rfc.monitor_timeout); chan->mps = le16_to_cpu(rfc.max_pdu_size); + + if (test_bit(FLAG_EFS_ENABLE, &chan->flags)) { + chan->local_msdu = le16_to_cpu(efs.msdu); + chan->local_sdu_itime = + le32_to_cpu(efs.sdu_itime); + chan->local_acc_lat = le32_to_cpu(efs.acc_lat); + chan->local_flush_to = + le32_to_cpu(efs.flush_to); + } break; + case L2CAP_MODE_STREAMING: chan->mps = le16_to_cpu(rfc.max_pdu_size); } -- cgit v1.2.3 From 2519a1fc82490eb13d69610f81fe84930f3b0e3f Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Mon, 7 Nov 2011 11:45:24 -0300 Subject: Bluetooth: Create hci_do_inquiry() This patch adds a function to hci_core to carry out inquiry. All inquiry code from start_discovery() were replaced by a hci_do_inquiry() call. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 18 ++++++++++++++++++ net/bluetooth/mgmt.c | 11 +++-------- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 006a7699abac..32f30533fd5d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -970,4 +970,6 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __u8 rand[8], void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); void hci_le_ltk_neg_reply(struct hci_conn *conn); +int hci_do_inquiry(struct hci_dev *hdev, u8 length); + #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b7f6b5bc1bb4..e6e991331ef8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2560,3 +2560,21 @@ static void hci_cmd_task(unsigned long arg) } } } + +int hci_do_inquiry(struct hci_dev *hdev, u8 length) +{ + /* General inquiry access code (GIAC) */ + u8 lap[3] = { 0x33, 0x8b, 0x9e }; + struct hci_cp_inquiry cp; + + BT_DBG("%s", hdev->name); + + if (test_bit(HCI_INQUIRY, &hdev->flags)) + return -EINPROGRESS; + + memset(&cp, 0, sizeof(cp)); + memcpy(&cp.lap, lap, sizeof(cp.lap)); + cp.length = length; + + return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); +} diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 747366a1f23c..17c7fbbc1210 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -32,6 +32,8 @@ #define MGMT_VERSION 0 #define MGMT_REVISION 1 +#define INQUIRY_LEN_BREDR 0x08 /* TGAP(100) */ + struct pending_cmd { struct list_head list; __u16 opcode; @@ -1598,8 +1600,6 @@ static int remove_remote_oob_data(struct sock *sk, u16 index, static int start_discovery(struct sock *sk, u16 index) { - u8 lap[3] = { 0x33, 0x8b, 0x9e }; - struct hci_cp_inquiry cp; struct pending_cmd *cmd; struct hci_dev *hdev; int err; @@ -1618,12 +1618,7 @@ static int start_discovery(struct sock *sk, u16 index) goto failed; } - memset(&cp, 0, sizeof(cp)); - memcpy(&cp.lap, lap, 3); - cp.length = 0x08; - cp.num_rsp = 0x00; - - err = hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); + err = hci_do_inquiry(hdev, INQUIRY_LEN_BREDR); if (err < 0) mgmt_pending_remove(cmd); -- cgit v1.2.3 From 023d50498d04c77b73eed11d849e436ef5639ed2 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 4 Nov 2011 14:16:52 -0300 Subject: Bluetooth: Create hci_cancel_inquiry() This patch adds a function to hci_core to cancel an ongoing inquiry. According to the Bluetooth spec, the inquiry cancel command should only be issued after the inquiry command has been issued, a command status event has been received for the inquiry command, and before the inquiry complete event occurs. As HCI_INQUIRY flag is only set just after an inquiry command status event occurs and it is cleared just after an inquiry complete event occurs, the inquiry cancel command should be issued only if HCI_INQUIRY flag is set. Additionally, cancel inquiry related code from stop_discovery() were replaced by a hci_cancel_inquiry() call. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 10 ++++++++++ net/bluetooth/mgmt.c | 2 +- 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 32f30533fd5d..20db034390b7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -971,5 +971,6 @@ void hci_le_ltk_reply(struct hci_conn *conn, u8 ltk[16]); void hci_le_ltk_neg_reply(struct hci_conn *conn); int hci_do_inquiry(struct hci_dev *hdev, u8 length); +int hci_cancel_inquiry(struct hci_dev *hdev); #endif /* __HCI_CORE_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e6e991331ef8..6a4bd2d8da99 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2578,3 +2578,13 @@ int hci_do_inquiry(struct hci_dev *hdev, u8 length) return hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); } + +int hci_cancel_inquiry(struct hci_dev *hdev) +{ + BT_DBG("%s", hdev->name); + + if (!test_bit(HCI_INQUIRY, &hdev->flags)) + return -EPERM; + + return hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); +} diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 17c7fbbc1210..0f9ef9432462 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1649,7 +1649,7 @@ static int stop_discovery(struct sock *sk, u16 index) goto failed; } - err = hci_send_cmd(hdev, HCI_OP_INQUIRY_CANCEL, 0, NULL); + err = hci_cancel_inquiry(hdev); if (err < 0) mgmt_pending_remove(cmd); -- cgit v1.2.3 From 89352e7d3ab372ffad8efe2aa070e0b63df42b85 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 4 Nov 2011 14:16:53 -0300 Subject: Bluetooth: Periodic Inquiry and Discovery By using periodic inquiry command we're not able to detect correctly when the controller has started inquiry. Today we have this workaround in inquiry result event handler to set the HCI_INQUIRY flag when it sees the first inquiry result event. This workaround isn't enough because the device may be performing an inquiry but the HCI_INQUIRY flag is not set. For instance, if there is no device in range, no inquiry result event is generated, consequently, the HCI_INQUIRY flags isn't set when it should so. We rely on HCI_INQUIRY flag to implement the discovery procedure properly. So, as we aren't able to clear/set the HCI_INQUIRY flag in a reliable manner, periodic inquiry events shouldn't change the HCI_INQUIRY flag. Thus, due to that issue and in order to keep compatibility with userspace, periodic inquiry events shouldn't send mgmt discovering events. In future, we might track if periodic inquiry is enabled or not. By tracking this state we'll be able to do some improvements in Discovery such as failing MGMT_OP_START_DISCOVERY command in case periodic inquiry is on. We can also send no mgmt_device_found event if periodic inquiry is on. Signed-off-by: Andre Guedes Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_event.c | 43 +++++++++++-------------------------------- 1 file changed, 11 insertions(+), 32 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9dc54db693a3..0c11203c261a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -58,9 +58,9 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 0); + clear_bit(HCI_INQUIRY, &hdev->flags); + + mgmt_discovering(hdev->id, 0); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); @@ -76,10 +76,6 @@ static void hci_cc_exit_periodic_inq(struct hci_dev *hdev, struct sk_buff *skb) if (status) return; - if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 0); - hci_conn_check_pending(hdev); } @@ -986,9 +982,9 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) return; } - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); + set_bit(HCI_INQUIRY, &hdev->flags); + + mgmt_discovering(hdev->id, 1); } static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) @@ -1367,13 +1363,14 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, status); - if (test_and_clear_bit(HCI_INQUIRY, &hdev->flags) && - test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 0); - hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); + + if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) + return; + + mgmt_discovering(hdev->id, 0); } static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -1389,12 +1386,6 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * hci_dev_lock(hdev); - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); - } - for (; num_rsp; num_rsp--, info++) { bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -2395,12 +2386,6 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct hci_dev_lock(hdev); - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); - } - if ((skb->len - 1) / num_rsp != sizeof(struct inquiry_info_with_rssi)) { struct inquiry_info_with_rssi_and_pscan_mode *info; info = (void *) (skb->data + 1); @@ -2563,12 +2548,6 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct if (!num_rsp) return; - if (!test_and_set_bit(HCI_INQUIRY, &hdev->flags)) { - - if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_discovering(hdev->id, 1); - } - hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { -- cgit v1.2.3 From 16ab91ab48287aa4fc757f3618820f728ee4412f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Nov 2011 22:16:02 +0200 Subject: Bluetooth: Add timeout field to mgmt_set_discoverable Based on the revised mgmt API set_discoverable has a timeout parameter to specify how long the adapter will remain discoverable. A value of 0 means "indefinitively". Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 +++ include/net/bluetooth/mgmt.h | 4 ++++ net/bluetooth/hci_core.c | 25 +++++++++++++++++++++++++ net/bluetooth/hci_event.c | 5 +++++ net/bluetooth/mgmt.c | 7 ++++++- 5 files changed, 43 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 20db034390b7..5803c1ebcefa 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -196,6 +196,9 @@ struct hci_dev { struct work_struct power_off; struct timer_list off_timer; + __u16 discov_timeout; + struct delayed_work discov_off; + struct timer_list cmd_timer; struct tasklet_struct cmd_task; struct tasklet_struct rx_task; diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3062fd3a65d2..b5320aa9b085 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -69,6 +69,10 @@ struct mgmt_mode { #define MGMT_OP_SET_POWERED 0x0005 #define MGMT_OP_SET_DISCOVERABLE 0x0006 +struct mgmt_cp_set_discoverable { + __u8 val; + __u16 timeout; +} __packed; #define MGMT_OP_SET_CONNECTABLE 0x0007 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6a4bd2d8da99..2da3f907e9b7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -595,6 +595,11 @@ static int hci_dev_do_close(struct hci_dev *hdev) tasklet_kill(&hdev->rx_task); tasklet_kill(&hdev->tx_task); + if (hdev->discov_timeout > 0) { + cancel_delayed_work_sync(&hdev->discov_off); + hdev->discov_timeout = 0; + } + hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); @@ -968,6 +973,24 @@ void hci_del_off_timer(struct hci_dev *hdev) del_timer(&hdev->off_timer); } +static void hci_discov_off(struct work_struct *work) +{ + struct hci_dev *hdev; + u8 scan = SCAN_PAGE; + + hdev = container_of(work, struct hci_dev, discov_off.work); + + BT_DBG("%s", hdev->name); + + hci_dev_lock_bh(hdev); + + hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, sizeof(scan), &scan); + + hdev->discov_timeout = 0; + + hci_dev_unlock_bh(hdev); +} + int hci_uuids_clear(struct hci_dev *hdev) { struct list_head *p, *n; @@ -1485,6 +1508,8 @@ int hci_register_dev(struct hci_dev *hdev) INIT_WORK(&hdev->power_off, hci_power_off); setup_timer(&hdev->off_timer, hci_auto_off, (unsigned long) hdev); + INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); + memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); atomic_set(&hdev->promisc, 0); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0c11203c261a..cf9926565937 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -292,6 +292,11 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) set_bit(HCI_ISCAN, &hdev->flags); if (!old_iscan) mgmt_discoverable(hdev->id, 1); + if (hdev->discov_timeout > 0) { + int to = msecs_to_jiffies(hdev->discov_timeout * 1000); + queue_delayed_work(hdev->workqueue, &hdev->discov_off, + to); + } } else if (old_iscan) mgmt_discoverable(hdev->id, 0); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0f9ef9432462..724d4fee2bd7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -350,7 +350,7 @@ failed: static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, u16 len) { - struct mgmt_mode *cp; + struct mgmt_cp_set_discoverable *cp; struct hci_dev *hdev; struct pending_cmd *cmd; u8 scan; @@ -396,11 +396,16 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, if (cp->val) scan |= SCAN_INQUIRY; + else + cancel_delayed_work_sync(&hdev->discov_off); err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) mgmt_pending_remove(cmd); + if (cp->val) + hdev->discov_timeout = get_unaligned_le16(&cp->timeout); + failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); -- cgit v1.2.3 From 2d7cee5836d6d466829b255b1290c9386d4e884f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Nov 2011 22:16:03 +0200 Subject: Bluetooth: Fix mgmt response when HCI_Write_Scan_Enable fails A proper mgmt_command_status should be returned to user-space if either discoverable or connectable enabling fails. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 9 ++++++--- net/bluetooth/mgmt.c | 13 +++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5803c1ebcefa..c233bceb3ccc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -911,6 +911,7 @@ int mgmt_index_removed(u16 index); int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); +int mgmt_write_scan_failed(u16 index, u8 scan, u8 status); int mgmt_new_key(u16 index, struct link_key *key, u8 persistent); int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index cf9926565937..176cecae4b42 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -280,11 +280,14 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; - if (status != 0) - goto done; - param = *((__u8 *) sent); + if (status != 0) { + mgmt_write_scan_failed(hdev->id, param, status); + hdev->discov_timeout = 0; + goto done; + } + old_pscan = test_and_clear_bit(HCI_PSCAN, &hdev->flags); old_iscan = test_and_clear_bit(HCI_ISCAN, &hdev->flags); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 724d4fee2bd7..0cb023e0edb4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2056,6 +2056,19 @@ int mgmt_connectable(u16 index, u8 connectable) return ret; } +int mgmt_write_scan_failed(u16 index, u8 scan, u8 status) +{ + if (scan & SCAN_PAGE) + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, + cmd_status_rsp, &status); + + if (scan & SCAN_INQUIRY) + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, + cmd_status_rsp, &status); + + return 0; +} + int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) { struct mgmt_ev_new_key ev; -- cgit v1.2.3 From 3243553fdc108a0ef49b9e25bdea9c87b341413e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Nov 2011 22:16:04 +0200 Subject: Bluetooth: Convert power off mechanism to use delayed_work The power off code doesn't need to use its own custom timer since the delayed_work API provides the exact same functionality. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 3 +-- net/bluetooth/hci_core.c | 39 +++++++++++++-------------------------- net/bluetooth/mgmt.c | 8 +++++--- 3 files changed, 19 insertions(+), 31 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c233bceb3ccc..bca53aa754e3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -193,8 +193,7 @@ struct hci_dev { struct workqueue_struct *workqueue; struct work_struct power_on; - struct work_struct power_off; - struct timer_list off_timer; + struct delayed_work power_off; __u16 discov_timeout; struct delayed_work discov_off; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2da3f907e9b7..e4ddf36d1701 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -600,6 +600,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) hdev->discov_timeout = 0; } + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work_sync(&hdev->power_off); + hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); @@ -819,7 +822,8 @@ int hci_get_dev_list(void __user *arg) read_lock_bh(&hci_dev_list_lock); list_for_each_entry(hdev, &hci_dev_list, list) { - hci_del_off_timer(hdev); + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work_sync(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->flags)) set_bit(HCI_PAIRABLE, &hdev->flags); @@ -854,7 +858,8 @@ int hci_get_dev_info(void __user *arg) if (!hdev) return -ENODEV; - hci_del_off_timer(hdev); + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work_sync(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->flags)) set_bit(HCI_PAIRABLE, &hdev->flags); @@ -938,8 +943,8 @@ static void hci_power_on(struct work_struct *work) return; if (test_bit(HCI_AUTO_OFF, &hdev->flags)) - mod_timer(&hdev->off_timer, - jiffies + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); + queue_delayed_work(hdev->workqueue, &hdev->power_off, + msecs_to_jiffies(AUTO_OFF_TIMEOUT)); if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) mgmt_index_added(hdev->id); @@ -947,30 +952,14 @@ static void hci_power_on(struct work_struct *work) static void hci_power_off(struct work_struct *work) { - struct hci_dev *hdev = container_of(work, struct hci_dev, power_off); - - BT_DBG("%s", hdev->name); - - hci_dev_close(hdev->id); -} - -static void hci_auto_off(unsigned long data) -{ - struct hci_dev *hdev = (struct hci_dev *) data; + struct hci_dev *hdev = container_of(work, struct hci_dev, + power_off.work); BT_DBG("%s", hdev->name); clear_bit(HCI_AUTO_OFF, &hdev->flags); - queue_work(hdev->workqueue, &hdev->power_off); -} - -void hci_del_off_timer(struct hci_dev *hdev) -{ - BT_DBG("%s", hdev->name); - - clear_bit(HCI_AUTO_OFF, &hdev->flags); - del_timer(&hdev->off_timer); + hci_dev_close(hdev->id); } static void hci_discov_off(struct work_struct *work) @@ -1505,8 +1494,7 @@ int hci_register_dev(struct hci_dev *hdev) (unsigned long) hdev); 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); + INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); @@ -1583,7 +1571,6 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_del_sysfs(hdev); - hci_del_off_timer(hdev); del_timer(&hdev->adv_timer); destroy_workqueue(hdev->workqueue); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0cb023e0edb4..6f9e3cd0d1fd 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -150,7 +150,8 @@ static int read_index_list(struct sock *sk) i = 0; list_for_each_entry(d, &hci_dev_list, list) { - hci_del_off_timer(d); + if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags)) + cancel_delayed_work_sync(&d->power_off); if (test_bit(HCI_SETUP, &d->flags)) continue; @@ -180,7 +181,8 @@ static int read_controller_info(struct sock *sk, u16 index) if (!hdev) return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV); - hci_del_off_timer(hdev); + if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) + cancel_delayed_work_sync(&hdev->power_off); hci_dev_lock_bh(hdev); @@ -337,7 +339,7 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) if (cp->val) queue_work(hdev->workqueue, &hdev->power_on); else - queue_work(hdev->workqueue, &hdev->power_off); + queue_work(hdev->workqueue, &hdev->power_off.work); err = 0; -- cgit v1.2.3 From 889d07ee57e950790cbec81df7b4f9d8691ee0b4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Nov 2011 12:25:52 +0200 Subject: Bluetooth: Remove redundant code from mgmt_block & mgmt_unblock There's no need to deal with mgmt_pending_cmd when blocking and unblocking devices since these actions are synchronous. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6f9e3cd0d1fd..e33b12e09270 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1671,7 +1671,6 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct pending_cmd *cmd; struct mgmt_cp_block_device *cp = (void *) data; int err; @@ -1688,23 +1687,13 @@ static int block_device(struct sock *sk, u16 index, unsigned char *data, hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_BLOCK_DEVICE, index, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - err = hci_blacklist_add(hdev, &cp->bdaddr); - if (err < 0) err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err); else err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE, NULL, 0); - mgmt_pending_remove(cmd); - -failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -1715,7 +1704,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; - struct pending_cmd *cmd; struct mgmt_cp_unblock_device *cp = (void *) data; int err; @@ -1732,12 +1720,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_UNBLOCK_DEVICE, index, NULL, 0); - if (!cmd) { - err = -ENOMEM; - goto failed; - } - err = hci_blacklist_del(hdev, &cp->bdaddr); if (err < 0) @@ -1746,9 +1728,6 @@ static int unblock_device(struct sock *sk, u16 index, unsigned char *data, err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE, NULL, 0); - mgmt_pending_remove(cmd); - -failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); -- cgit v1.2.3 From bd2d1334e1dd64765b29f9e1b592777c410ed121 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Nov 2011 23:13:37 +0200 Subject: Bluetooth: Fix response for mgmt_start_discovery when powered off We should return a ENETDOWN status response if the adapter is powered off (i.e. the HCI_UP flag isn't set). Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e33b12e09270..af077abdfa98 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1619,6 +1619,11 @@ static int start_discovery(struct sock *sk, u16 index) hci_dev_lock_bh(hdev); + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, MGMT_OP_START_DISCOVERY, ENETDOWN); + goto failed; + } + cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); if (!cmd) { err = -ENOMEM; -- cgit v1.2.3 From 86742e1eca319069490f6f20c2892baafc2a6922 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Nov 2011 23:13:38 +0200 Subject: Bluetooth: Update link key mgmt APIs to match latest spec. BR/EDR link keys have their own commands and events (separate from SMP) and the remove_keys command (previously remove_key) removes keys of any kind for the specified remote address. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 +- include/net/bluetooth/mgmt.h | 18 ++++++++--------- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 43 +++++++++++++++++++++------------------- 4 files changed, 35 insertions(+), 32 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bca53aa754e3..4ebc882385f9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -911,7 +911,7 @@ int mgmt_powered(u16 index, u8 powered); int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); int mgmt_write_scan_failed(u16 index, u8 scan, u8 status); -int mgmt_new_key(u16 index, struct link_key *key, u8 persistent); +int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent); int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); int mgmt_disconnect_failed(u16 index); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index b5320aa9b085..fa33bc6c485f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -100,22 +100,22 @@ struct mgmt_cp_set_service_cache { __u8 enable; } __packed; -struct mgmt_key_info { +struct mgmt_link_key_info { bdaddr_t bdaddr; u8 type; u8 val[16]; u8 pin_len; } __packed; -#define MGMT_OP_LOAD_KEYS 0x000D -struct mgmt_cp_load_keys { +#define MGMT_OP_LOAD_LINK_KEYS 0x000D +struct mgmt_cp_load_link_keys { __u8 debug_keys; __le16 key_count; - struct mgmt_key_info keys[0]; + struct mgmt_link_key_info keys[0]; } __packed; -#define MGMT_OP_REMOVE_KEY 0x000E -struct mgmt_cp_remove_key { +#define MGMT_OP_REMOVE_KEYS 0x000E +struct mgmt_cp_remove_keys { bdaddr_t bdaddr; __u8 disconnect; } __packed; @@ -247,10 +247,10 @@ struct mgmt_ev_controller_error { #define MGMT_EV_PAIRABLE 0x0009 -#define MGMT_EV_NEW_KEY 0x000A -struct mgmt_ev_new_key { +#define MGMT_EV_NEW_LINK_KEY 0x000A +struct mgmt_ev_new_link_key { __u8 store_hint; - struct mgmt_key_info key; + struct mgmt_link_key_info key; } __packed; #define MGMT_EV_CONNECTED 0x000B diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e4ddf36d1701..693c0dfc6b9d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1140,7 +1140,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, persistent = hci_persistent_key(hdev, conn, type, old_key_type); - mgmt_new_key(hdev->id, key, persistent); + mgmt_new_link_key(hdev->id, key, persistent); if (!persistent) { list_del(&key->list); @@ -1183,7 +1183,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, memcpy(id->rand, rand, sizeof(id->rand)); if (new_key) - mgmt_new_key(hdev->id, key, old_key_type); + mgmt_new_link_key(hdev->id, key, old_key_type); return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index af077abdfa98..1939053c3fcd 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -908,30 +908,32 @@ static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, return err; } -static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) +static int load_link_keys(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; - struct mgmt_cp_load_keys *cp; + struct mgmt_cp_load_link_keys *cp; u16 key_count, expected_len; int i; cp = (void *) data; if (len < sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, EINVAL); + return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL); key_count = get_unaligned_le16(&cp->key_count); - expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); + expected_len = sizeof(*cp) + key_count * + sizeof(struct mgmt_link_key_info); if (expected_len != len) { - BT_ERR("load_keys: expected %u bytes, got %u bytes", + BT_ERR("load_link_keys: expected %u bytes, got %u bytes", len, expected_len); - return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, EINVAL); + return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, EINVAL); } hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV); + return cmd_status(sk, index, MGMT_OP_LOAD_LINK_KEYS, ENODEV); BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys, key_count); @@ -948,7 +950,7 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) clear_bit(HCI_DEBUG_KEYS, &hdev->flags); for (i = 0; i < key_count; i++) { - struct mgmt_key_info *key = &cp->keys[i]; + struct mgmt_link_key_info *key = &cp->keys[i]; hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type, key->pin_len); @@ -960,27 +962,28 @@ static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) return 0; } -static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) +static int remove_keys(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; - struct mgmt_cp_remove_key *cp; + struct mgmt_cp_remove_keys *cp; struct hci_conn *conn; int err; cp = (void *) data; if (len != sizeof(*cp)) - return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, EINVAL); + return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, EINVAL); hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, ENODEV); hci_dev_lock_bh(hdev); err = hci_remove_link_key(hdev, &cp->bdaddr); if (err < 0) { - err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err); + err = cmd_status(sk, index, MGMT_OP_REMOVE_KEYS, -err); goto unlock; } @@ -1860,11 +1863,11 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) case MGMT_OP_SET_SERVICE_CACHE: err = set_service_cache(sk, index, buf + sizeof(*hdr), len); break; - case MGMT_OP_LOAD_KEYS: - err = load_keys(sk, index, buf + sizeof(*hdr), len); + case MGMT_OP_LOAD_LINK_KEYS: + err = load_link_keys(sk, index, buf + sizeof(*hdr), len); break; - case MGMT_OP_REMOVE_KEY: - err = remove_key(sk, index, buf + sizeof(*hdr), len); + case MGMT_OP_REMOVE_KEYS: + err = remove_keys(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_DISCONNECT: err = disconnect(sk, index, buf + sizeof(*hdr), len); @@ -2055,9 +2058,9 @@ int mgmt_write_scan_failed(u16 index, u8 scan, u8 status) return 0; } -int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) +int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent) { - struct mgmt_ev_new_key ev; + struct mgmt_ev_new_link_key ev; memset(&ev, 0, sizeof(ev)); @@ -2067,7 +2070,7 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 persistent) memcpy(ev.key.val, key->val, 16); ev.key.pin_len = key->pin_len; - return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_NEW_LINK_KEY, index, &ev, sizeof(ev), NULL); } int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) -- cgit v1.2.3 From 4c659c3976e81f9def48993cd00988d53d7379f2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Nov 2011 23:13:39 +0200 Subject: Bluetooth: Add address type fields to mgmt messages that need them This patch adds address type info (typically BR/EDR vs LE) to management messages that need this. This also ensures conformance to the latest management API specification. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 10 ++++----- include/net/bluetooth/mgmt.h | 23 +++++++++++--------- net/bluetooth/hci_event.c | 20 +++++++++-------- net/bluetooth/mgmt.c | 47 ++++++++++++++++++++++++++++++---------- 4 files changed, 64 insertions(+), 36 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4ebc882385f9..e6071d0ea20f 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -912,10 +912,10 @@ int mgmt_discoverable(u16 index, u8 discoverable); int mgmt_connectable(u16 index, u8 connectable); int mgmt_write_scan_failed(u16 index, u8 scan, u8 status); int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent); -int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type); -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr); +int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 type); +int mgmt_disconnected(u16 index, bdaddr_t *bdaddr, u8 type); int mgmt_disconnect_failed(u16 index); -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status); +int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 type, u8 status); int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure); int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); @@ -928,8 +928,8 @@ int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status); int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status); int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, u8 status); -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, - u8 *eir); +int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 *dev_class, + s8 rssi, u8 *eir); int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); int mgmt_inquiry_failed(u16 index, u8 status); int mgmt_discovering(u16 index, u8 discovering); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index fa33bc6c485f..3e320c9cae8f 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -128,10 +128,20 @@ struct mgmt_rp_disconnect { bdaddr_t bdaddr; } __packed; +#define MGMT_ADDR_BREDR 0x00 +#define MGMT_ADDR_LE 0x01 +#define MGMT_ADDR_BREDR_LE 0x02 +#define MGMT_ADDR_INVALID 0xff + +struct mgmt_addr_info { + bdaddr_t bdaddr; + __u8 type; +} __packed; + #define MGMT_OP_GET_CONNECTIONS 0x0010 struct mgmt_rp_get_connections { __le16 conn_count; - bdaddr_t conn[0]; + struct mgmt_addr_info addr[0]; } __packed; #define MGMT_OP_PIN_CODE_REPLY 0x0011 @@ -254,19 +264,12 @@ struct mgmt_ev_new_link_key { } __packed; #define MGMT_EV_CONNECTED 0x000B -struct mgmt_ev_connected { - bdaddr_t bdaddr; - __u8 link_type; -} __packed; #define MGMT_EV_DISCONNECTED 0x000C -struct mgmt_ev_disconnected { - bdaddr_t bdaddr; -} __packed; #define MGMT_EV_CONNECT_FAILED 0x000D struct mgmt_ev_connect_failed { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 status; } __packed; @@ -296,7 +299,7 @@ struct mgmt_ev_local_name_changed { #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { - bdaddr_t bdaddr; + struct mgmt_addr_info addr; __u8 dev_class[3]; __s8 rssi; __u8 eir[HCI_MAX_EIR_LENGTH]; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 176cecae4b42..2fced8c43258 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1404,8 +1404,8 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * data.rssi = 0x00; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, 0, - NULL); + mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, + info->dev_class, 0, NULL); } hci_dev_unlock(hdev); @@ -1471,7 +1471,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s } else { conn->state = BT_CLOSED; if (conn->type == ACL_LINK) - mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); + mgmt_connect_failed(hdev->id, &ev->bdaddr, conn->type, + ev->status); } if (conn->type == ACL_LINK) @@ -1584,7 +1585,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff conn->state = BT_CLOSED; if (conn->type == ACL_LINK || conn->type == LE_LINK) - mgmt_disconnected(hdev->id, &conn->dst); + mgmt_disconnected(hdev->id, &conn->dst, conn->type); hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); @@ -2408,7 +2409,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, + mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, info->dev_class, info->rssi, NULL); } @@ -2425,7 +2426,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, + mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, info->dev_class, info->rssi, NULL); } @@ -2568,8 +2569,8 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x01; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, info->dev_class, - info->rssi, info->data); + mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, + info->dev_class, info->rssi, info->data); } hci_dev_unlock(hdev); @@ -2832,7 +2833,8 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff } if (ev->status) { - mgmt_connect_failed(hdev->id, &ev->bdaddr, ev->status); + mgmt_connect_failed(hdev->id, &ev->bdaddr, conn->type, + ev->status); hci_proto_connect_cfm(conn, ev->status); conn->state = BT_CLOSED; hci_conn_del(conn); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1939053c3fcd..4cb2f958fb10 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1069,6 +1069,18 @@ failed: return err; } +static u8 link_to_mgmt(u8 link_type) +{ + switch (link_type) { + case LE_LINK: + return MGMT_ADDR_LE; + case ACL_LINK: + return MGMT_ADDR_BREDR; + default: + return MGMT_ADDR_INVALID; + } +} + static int get_connections(struct sock *sk, u16 index) { struct mgmt_rp_get_connections *rp; @@ -1092,7 +1104,7 @@ static int get_connections(struct sock *sk, u16 index) count++; } - rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t)); + rp_len = sizeof(*rp) + (count * sizeof(struct mgmt_addr_info)); rp = kmalloc(rp_len, GFP_ATOMIC); if (!rp) { err = -ENOMEM; @@ -1102,8 +1114,16 @@ static int get_connections(struct sock *sk, u16 index) put_unaligned_le16(count, &rp->conn_count); i = 0; - list_for_each_entry(c, &hdev->conn_hash.list, list) - bacpy(&rp->conn[i++], &c->dst); + list_for_each_entry(c, &hdev->conn_hash.list, list) { + bacpy(&rp->addr[i].bdaddr, &c->dst); + rp->addr[i].type = link_to_mgmt(c->type); + if (rp->addr[i].type == MGMT_ADDR_INVALID) + continue; + i++; + } + + /* Recalculate length in case of filtered SCO connections, etc */ + rp_len = sizeof(*rp) + (i * sizeof(struct mgmt_addr_info)); err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len); @@ -2075,10 +2095,10 @@ int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent) int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) { - struct mgmt_ev_connected ev; + struct mgmt_addr_info ev; bacpy(&ev.bdaddr, bdaddr); - ev.link_type = link_type; + ev.type = link_to_mgmt(link_type); return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); } @@ -2099,15 +2119,16 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) +int mgmt_disconnected(u16 index, bdaddr_t *bdaddr, u8 type) { - struct mgmt_ev_disconnected ev; + struct mgmt_addr_info ev; struct sock *sk = NULL; int err; mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); bacpy(&ev.bdaddr, bdaddr); + ev.type = link_to_mgmt(type); err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk); @@ -2133,11 +2154,12 @@ int mgmt_disconnect_failed(u16 index) return err; } -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 type, u8 status) { struct mgmt_ev_connect_failed ev; - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(type); ev.status = status; return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); @@ -2325,14 +2347,15 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, return err; } -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 *dev_class, s8 rssi, - u8 *eir) +int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 *dev_class, + s8 rssi, u8 *eir) { struct mgmt_ev_device_found ev; memset(&ev, 0, sizeof(ev)); - bacpy(&ev.bdaddr, bdaddr); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_mgmt(type); ev.rssi = rssi; if (eir) -- cgit v1.2.3 From 744cf19eadcf4de914394e0eb227f94f4318f5e4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Nov 2011 20:40:14 +0200 Subject: Bluetooth: Pass full hci_dev struct to mgmt callbacks The current global pending command list in mgmt.c is racy. Possibly the simplest way to fix it is to have per-hci dev lists instead of a global one (all commands that need a pending struct are hci_dev specific). This way the list can be protected using the already existing per-hci dev lock. To enable this refactoring the first thing that needs to be done is to ensure that the mgmt functions have access to the hci_dev struct (instead of just the dev id). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 61 ++++++------ net/bluetooth/hci_core.c | 16 +-- net/bluetooth/hci_event.c | 63 ++++++------ net/bluetooth/mgmt.c | 206 ++++++++++++++++++++------------------- 4 files changed, 178 insertions(+), 168 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e6071d0ea20f..0f35a3900e16 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -905,36 +905,41 @@ void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb, /* Management interface */ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); -int mgmt_index_added(u16 index); -int mgmt_index_removed(u16 index); -int mgmt_powered(u16 index, u8 powered); -int mgmt_discoverable(u16 index, u8 discoverable); -int mgmt_connectable(u16 index, u8 connectable); -int mgmt_write_scan_failed(u16 index, u8 scan, u8 status); -int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent); -int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 type); -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr, u8 type); -int mgmt_disconnect_failed(u16 index); -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 type, u8 status); -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure); -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value, - u8 confirm_hint); -int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, +int mgmt_index_added(struct hci_dev *hdev); +int mgmt_index_removed(struct hci_dev *hdev); +int mgmt_powered(struct hci_dev *hdev, u8 powered); +int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); +int mgmt_connectable(struct hci_dev *hdev, u8 connectable); +int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + u8 persistent); +int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +int mgmt_disconnect_failed(struct hci_dev *hdev); +int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u8 status); -int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status); -int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status); -int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, +int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure); +int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 *dev_class, - s8 rssi, u8 *eir); -int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name); -int mgmt_inquiry_failed(u16 index, u8 status); -int mgmt_discovering(u16 index, u8 discovering); -int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr); -int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr); +int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + __le32 value, u8 confirm_hint); +int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status); +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status); +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status); +int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status); +int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status); +int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 *dev_class, s8 rssi, u8 *eir); +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name); +int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status); +int mgmt_discovering(struct hci_dev *hdev, u8 discovering); +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr); +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr); /* HCI info for socket */ #define hci_pi(sk) ((struct hci_pinfo *) sk) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 693c0dfc6b9d..e4b5c6345095 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -550,7 +550,7 @@ int hci_dev_open(__u16 dev) set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->flags)) - mgmt_powered(hdev->id, 1); + mgmt_powered(hdev, 1); } else { /* Init failed, cleanup */ tasklet_kill(&hdev->rx_task); @@ -642,7 +642,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); - mgmt_powered(hdev->id, 0); + mgmt_powered(hdev, 0); /* Clear flags */ hdev->flags = 0; @@ -947,7 +947,7 @@ static void hci_power_on(struct work_struct *work) msecs_to_jiffies(AUTO_OFF_TIMEOUT)); if (test_and_clear_bit(HCI_SETUP, &hdev->flags)) - mgmt_index_added(hdev->id); + mgmt_index_added(hdev); } static void hci_power_off(struct work_struct *work) @@ -1140,7 +1140,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, persistent = hci_persistent_key(hdev, conn, type, old_key_type); - mgmt_new_link_key(hdev->id, key, persistent); + mgmt_new_link_key(hdev, key, persistent); if (!persistent) { list_del(&key->list); @@ -1183,7 +1183,7 @@ int hci_add_ltk(struct hci_dev *hdev, int new_key, bdaddr_t *bdaddr, memcpy(id->rand, rand, sizeof(id->rand)); if (new_key) - mgmt_new_link_key(hdev->id, key, old_key_type); + mgmt_new_link_key(hdev, key, old_key_type); return 0; } @@ -1324,7 +1324,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr) list_add(&entry->list, &hdev->blacklist); - return mgmt_device_blocked(hdev->id, bdaddr); + return mgmt_device_blocked(hdev, bdaddr); } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) @@ -1343,7 +1343,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr) list_del(&entry->list); kfree(entry); - return mgmt_device_unblocked(hdev->id, bdaddr); + return mgmt_device_unblocked(hdev, bdaddr); } static void hci_clear_adv_cache(unsigned long arg) @@ -1560,7 +1560,7 @@ void hci_unregister_dev(struct hci_dev *hdev) if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->flags)) - mgmt_index_removed(hdev->id); + mgmt_index_removed(hdev); hci_notify(hdev, HCI_DEV_UNREG); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2fced8c43258..8303f8fa1821 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -60,7 +60,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_INQUIRY, &hdev->flags); - mgmt_discovering(hdev->id, 0); + mgmt_discovering(hdev, 0); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); @@ -202,7 +202,7 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) return; if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_set_local_name_complete(hdev->id, sent, status); + mgmt_set_local_name_complete(hdev, sent, status); if (status) return; @@ -283,7 +283,7 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) param = *((__u8 *) sent); if (status != 0) { - mgmt_write_scan_failed(hdev->id, param, status); + mgmt_write_scan_failed(hdev, param, status); hdev->discov_timeout = 0; goto done; } @@ -294,21 +294,21 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) if (param & SCAN_INQUIRY) { set_bit(HCI_ISCAN, &hdev->flags); if (!old_iscan) - mgmt_discoverable(hdev->id, 1); + mgmt_discoverable(hdev, 1); if (hdev->discov_timeout > 0) { int to = msecs_to_jiffies(hdev->discov_timeout * 1000); queue_delayed_work(hdev->workqueue, &hdev->discov_off, to); } } else if (old_iscan) - mgmt_discoverable(hdev->id, 0); + mgmt_discoverable(hdev, 0); if (param & SCAN_PAGE) { set_bit(HCI_PSCAN, &hdev->flags); if (!old_pscan) - mgmt_connectable(hdev->id, 1); + mgmt_connectable(hdev, 1); } else if (old_pscan) - mgmt_connectable(hdev->id, 0); + mgmt_connectable(hdev, 0); done: hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); @@ -835,7 +835,7 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_reply_complete(hdev->id, &rp->bdaddr, rp->status); + mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); if (rp->status != 0) return; @@ -856,7 +856,7 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_pin_code_neg_reply_complete(hdev->id, &rp->bdaddr, + mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, rp->status); } static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, @@ -886,7 +886,7 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_user_confirm_reply_complete(hdev->id, &rp->bdaddr, + mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, rp->status); } @@ -898,7 +898,7 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, rp->status); if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_user_confirm_neg_reply_complete(hdev->id, &rp->bdaddr, + mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, rp->status); } @@ -909,7 +909,7 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, rp->status); - mgmt_read_local_oob_data_reply_complete(hdev->id, rp->hash, + mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, rp->randomizer, rp->status); } @@ -986,13 +986,13 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); if (test_bit(HCI_MGMT, &hdev->flags)) - mgmt_inquiry_failed(hdev->id, status); + mgmt_inquiry_failed(hdev, status); return; } set_bit(HCI_INQUIRY, &hdev->flags); - mgmt_discovering(hdev->id, 1); + mgmt_discovering(hdev, 1); } static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) @@ -1378,7 +1378,7 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) return; - mgmt_discovering(hdev->id, 0); + mgmt_discovering(hdev, 0); } static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -1404,7 +1404,7 @@ static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff * data.rssi = 0x00; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, info->dev_class, 0, NULL); } @@ -1439,7 +1439,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->state = BT_CONFIG; hci_conn_hold(conn); conn->disc_timeout = HCI_DISCONN_TIMEOUT; - mgmt_connected(hdev->id, &ev->bdaddr, conn->type); + mgmt_connected(hdev, &ev->bdaddr, conn->type); } else conn->state = BT_CONNECTED; @@ -1471,7 +1471,7 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s } else { conn->state = BT_CLOSED; if (conn->type == ACL_LINK) - mgmt_connect_failed(hdev->id, &ev->bdaddr, conn->type, + mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status); } @@ -1572,7 +1572,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, ev->status); if (ev->status) { - mgmt_disconnect_failed(hdev->id); + mgmt_disconnect_failed(hdev); return; } @@ -1585,7 +1585,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff conn->state = BT_CLOSED; if (conn->type == ACL_LINK || conn->type == LE_LINK) - mgmt_disconnected(hdev->id, &conn->dst, conn->type); + mgmt_disconnected(hdev, &conn->dst, conn->type); hci_proto_disconn_cfm(conn, ev->reason); hci_conn_del(conn); @@ -1616,7 +1616,7 @@ static inline void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *s conn->sec_level = conn->pending_sec_level; } } else { - mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + mgmt_auth_failed(hdev, &conn->dst, ev->status); } clear_bit(HCI_CONN_AUTH_PEND, &conn->pend); @@ -1671,7 +1671,7 @@ static inline void hci_remote_name_evt(struct hci_dev *hdev, struct sk_buff *skb hci_dev_lock(hdev); if (ev->status == 0 && test_bit(HCI_MGMT, &hdev->flags)) - mgmt_remote_name(hdev->id, &ev->bdaddr, ev->name); + mgmt_remote_name(hdev, &ev->bdaddr, ev->name); conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (!conn) @@ -2061,7 +2061,7 @@ static inline void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) case HCI_OP_DISCONNECT: if (ev->status != 0) - mgmt_disconnect_failed(hdev->id); + mgmt_disconnect_failed(hdev); break; case HCI_OP_LE_CREATE_CONN: @@ -2226,7 +2226,7 @@ static inline void hci_pin_code_request_evt(struct hci_dev *hdev, struct sk_buff else secure = 0; - mgmt_pin_code_request(hdev->id, &ev->bdaddr, secure); + mgmt_pin_code_request(hdev, &ev->bdaddr, secure); } unlock: @@ -2409,7 +2409,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, info->dev_class, info->rssi, NULL); } @@ -2426,7 +2426,7 @@ static inline void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x00; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, info->dev_class, info->rssi, NULL); } @@ -2569,7 +2569,7 @@ static inline void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct data.rssi = info->rssi; data.ssp_mode = 0x01; hci_inquiry_cache_update(hdev, &data); - mgmt_device_found(hdev->id, &info->bdaddr, ACL_LINK, + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, info->dev_class, info->rssi, info->data); } @@ -2726,7 +2726,7 @@ static inline void hci_user_confirm_request_evt(struct hci_dev *hdev, } confirm: - mgmt_user_confirm_request(hdev->id, &ev->bdaddr, ev->passkey, + mgmt_user_confirm_request(hdev, &ev->bdaddr, ev->passkey, confirm_hint); unlock: @@ -2752,7 +2752,7 @@ static inline void hci_simple_pair_complete_evt(struct hci_dev *hdev, struct sk_ * event gets always produced as initiator and is also mapped to * the mgmt_auth_failed event */ if (!test_bit(HCI_CONN_AUTH_PEND, &conn->pend) && ev->status != 0) - mgmt_auth_failed(hdev->id, &conn->dst, ev->status); + mgmt_auth_failed(hdev, &conn->dst, ev->status); hci_conn_put(conn); @@ -2833,15 +2833,14 @@ static inline void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff } if (ev->status) { - mgmt_connect_failed(hdev->id, &ev->bdaddr, conn->type, - ev->status); + mgmt_connect_failed(hdev, &ev->bdaddr, conn->type, ev->status); hci_proto_connect_cfm(conn, ev->status); conn->state = BT_CLOSED; hci_conn_del(conn); goto unlock; } - mgmt_connected(hdev->id, &ev->bdaddr, conn->type); + mgmt_connected(hdev, &ev->bdaddr, conn->type); conn->sec_level = BT_SECURITY_LOW; conn->handle = __le16_to_cpu(ev->handle); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4cb2f958fb10..2ca7b4427e34 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -255,7 +255,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, return cmd; } -static void mgmt_pending_foreach(u16 opcode, int index, +static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, void (*cb)(struct pending_cmd *cmd, void *data), void *data) { @@ -269,7 +269,7 @@ static void mgmt_pending_foreach(u16 opcode, int index, if (opcode > 0 && cmd->opcode != opcode) continue; - if (index >= 0 && cmd->index != index) + if (hdev && cmd->index != hdev->id) continue; cb(cmd, data); @@ -475,8 +475,8 @@ failed: return err; } -static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, - struct sock *skip_sk) +static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, + u16 data_len, struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -489,7 +489,10 @@ static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(event); - hdr->index = cpu_to_le16(index); + if (hdev) + hdr->index = cpu_to_le16(hdev->id); + else + hdr->index = cpu_to_le16(MGMT_INDEX_NONE); hdr->len = cpu_to_le16(data_len); if (data) @@ -541,7 +544,7 @@ static int set_pairable(struct sock *sk, u16 index, unsigned char *data, ev.val = cp->val; - err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_PAIRABLE, hdev, &ev, sizeof(ev), sk); failed: hci_dev_unlock_bh(hdev); @@ -1966,18 +1969,18 @@ static void cmd_status_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -int mgmt_index_added(u16 index) +int mgmt_index_added(struct hci_dev *hdev) { - return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL); + return mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } -int mgmt_index_removed(u16 index) +int mgmt_index_removed(struct hci_dev *hdev) { u8 status = ENODEV; - mgmt_pending_foreach(0, index, cmd_status_rsp, &status); + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); + return mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); } struct cmd_lookup { @@ -2005,22 +2008,22 @@ static void mode_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_free(cmd); } -int mgmt_powered(u16 index, u8 powered) +int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct mgmt_mode ev; struct cmd_lookup match = { powered, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, mode_rsp, &match); if (!powered) { u8 status = ENETDOWN; - mgmt_pending_foreach(0, index, cmd_status_rsp, &status); + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); } ev.val = powered; - ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_POWERED, hdev, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -2028,17 +2031,17 @@ int mgmt_powered(u16 index, u8 powered) return ret; } -int mgmt_discoverable(u16 index, u8 discoverable) +int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) { struct mgmt_mode ev; struct cmd_lookup match = { discoverable, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, mode_rsp, &match); ev.val = discoverable; - ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev), + ret = mgmt_event(MGMT_EV_DISCOVERABLE, hdev, &ev, sizeof(ev), match.sk); if (match.sk) @@ -2047,17 +2050,17 @@ int mgmt_discoverable(u16 index, u8 discoverable) return ret; } -int mgmt_connectable(u16 index, u8 connectable) +int mgmt_connectable(struct hci_dev *hdev, u8 connectable) { struct mgmt_mode ev; struct cmd_lookup match = { connectable, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, mode_rsp, &match); ev.val = connectable; - ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_CONNECTABLE, hdev, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -2065,20 +2068,21 @@ int mgmt_connectable(u16 index, u8 connectable) return ret; } -int mgmt_write_scan_failed(u16 index, u8 scan, u8 status) +int mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { if (scan & SCAN_PAGE) - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, + mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, cmd_status_rsp, &status); if (scan & SCAN_INQUIRY) - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, cmd_status_rsp, &status); return 0; } -int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent) +int mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, + u8 persistent) { struct mgmt_ev_new_link_key ev; @@ -2090,17 +2094,17 @@ int mgmt_new_link_key(u16 index, struct link_key *key, u8 persistent) memcpy(ev.key.val, key->val, 16); ev.key.pin_len = key->pin_len; - return mgmt_event(MGMT_EV_NEW_LINK_KEY, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); } -int mgmt_connected(u16 index, bdaddr_t *bdaddr, u8 link_type) +int mgmt_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type) { struct mgmt_addr_info ev; bacpy(&ev.bdaddr, bdaddr); ev.type = link_to_mgmt(link_type); - return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECTED, hdev, &ev, sizeof(ev), NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) @@ -2119,18 +2123,18 @@ static void disconnect_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_remove(cmd); } -int mgmt_disconnected(u16 index, bdaddr_t *bdaddr, u8 type) +int mgmt_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) { struct mgmt_addr_info ev; struct sock *sk = NULL; int err; - mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); + mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); bacpy(&ev.bdaddr, bdaddr); ev.type = link_to_mgmt(type); - err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_DISCONNECTED, hdev, &ev, sizeof(ev), sk); if (sk) sock_put(sk); @@ -2138,23 +2142,24 @@ int mgmt_disconnected(u16 index, bdaddr_t *bdaddr, u8 type) return err; } -int mgmt_disconnect_failed(u16 index) +int mgmt_disconnect_failed(struct hci_dev *hdev) { struct pending_cmd *cmd; int err; - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, index); + cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev->id); if (!cmd) return -ENOENT; - err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO); + err = cmd_status(cmd->sk, hdev->id, MGMT_OP_DISCONNECT, EIO); mgmt_pending_remove(cmd); return err; } -int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 type, u8 status) +int mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 status) { struct mgmt_ev_connect_failed ev; @@ -2162,34 +2167,35 @@ int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 type, u8 status) ev.addr.type = link_to_mgmt(type); ev.status = status; - return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr, u8 secure) +int mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) { struct mgmt_ev_pin_code_request ev; bacpy(&ev.bdaddr, bdaddr); ev.secure = secure; - return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev), + return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL); } -int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; int err; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev->id); if (!cmd) return -ENOENT; bacpy(&rp.bdaddr, bdaddr); rp.status = status; - err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -2197,20 +2203,21 @@ int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) return err; } -int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { struct pending_cmd *cmd; struct mgmt_rp_pin_code_reply rp; int err; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev->id); if (!cmd) return -ENOENT; bacpy(&rp.bdaddr, bdaddr); rp.status = status; - err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, sizeof(rp)); mgmt_pending_remove(cmd); @@ -2218,97 +2225,95 @@ int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) return err; } -int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value, - u8 confirm_hint) +int mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, + __le32 value, u8 confirm_hint) { struct mgmt_ev_user_confirm_request ev; - BT_DBG("hci%u", index); + BT_DBG("%s", hdev->name); bacpy(&ev.bdaddr, bdaddr); ev.confirm_hint = confirm_hint; put_unaligned_le32(value, &ev.value); - return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev), + return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), NULL); } -static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, - u8 opcode) +static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status, u8 opcode) { struct pending_cmd *cmd; struct mgmt_rp_user_confirm_reply rp; int err; - cmd = mgmt_pending_find(opcode, index); + cmd = mgmt_pending_find(opcode, hdev->id); if (!cmd) return -ENOENT; bacpy(&rp.bdaddr, bdaddr); rp.status = status; - err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, hdev->id, opcode, &rp, sizeof(rp)); mgmt_pending_remove(cmd); return err; } -int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 status) { - return confirm_reply_complete(index, bdaddr, status, + return confirm_reply_complete(hdev, bdaddr, status, MGMT_OP_USER_CONFIRM_REPLY); } -int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 status) { - return confirm_reply_complete(index, bdaddr, status, + return confirm_reply_complete(hdev, bdaddr, status, MGMT_OP_USER_CONFIRM_NEG_REPLY); } -int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) +int mgmt_auth_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 status) { struct mgmt_ev_auth_failed ev; bacpy(&ev.bdaddr, bdaddr); ev.status = status; - return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_AUTH_FAILED, hdev, &ev, sizeof(ev), NULL); } -int mgmt_set_local_name_complete(u16 index, u8 *name, u8 status) +int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { struct pending_cmd *cmd; - struct hci_dev *hdev; struct mgmt_cp_set_local_name ev; int err; memset(&ev, 0, sizeof(ev)); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, index); + cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev->id); if (!cmd) goto send_event; if (status) { - err = cmd_status(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, EIO); + err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, + EIO); goto failed; } - hdev = hci_dev_get(index); - if (hdev) { - hci_dev_lock_bh(hdev); - update_eir(hdev); - hci_dev_unlock_bh(hdev); - hci_dev_put(hdev); - } + hci_dev_lock_bh(hdev); + update_eir(hdev); + hci_dev_unlock_bh(hdev); - err = cmd_complete(cmd->sk, index, MGMT_OP_SET_LOCAL_NAME, &ev, + err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev, sizeof(ev)); if (err < 0) goto failed; send_event: - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, index, &ev, sizeof(ev), + err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), cmd ? cmd->sk : NULL); failed: @@ -2317,29 +2322,30 @@ failed: return err; } -int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, - u8 status) +int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, + u8 *randomizer, u8 status) { struct pending_cmd *cmd; int err; - BT_DBG("hci%u status %u", index, status); + BT_DBG("%s status %u", hdev->name, status); - cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index); + cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev->id); if (!cmd) return -ENOENT; if (status) { - err = cmd_status(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - EIO); + err = cmd_status(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, EIO); } else { struct mgmt_rp_read_local_oob_data rp; memcpy(rp.hash, hash, sizeof(rp.hash)); memcpy(rp.randomizer, randomizer, sizeof(rp.randomizer)); - err = cmd_complete(cmd->sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, - &rp, sizeof(rp)); + err = cmd_complete(cmd->sk, hdev->id, + MGMT_OP_READ_LOCAL_OOB_DATA, + &rp, sizeof(rp)); } mgmt_pending_remove(cmd); @@ -2347,8 +2353,8 @@ int mgmt_read_local_oob_data_reply_complete(u16 index, u8 *hash, u8 *randomizer, return err; } -int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 *dev_class, - s8 rssi, u8 *eir) +int mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, + u8 *dev_class, s8 rssi, u8 *eir) { struct mgmt_ev_device_found ev; @@ -2364,10 +2370,10 @@ int mgmt_device_found(u16 index, bdaddr_t *bdaddr, u8 type, u8 *dev_class, if (dev_class) memcpy(ev.dev_class, dev_class, sizeof(ev.dev_class)); - return mgmt_event(MGMT_EV_DEVICE_FOUND, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, &ev, sizeof(ev), NULL); } -int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) +int mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *name) { struct mgmt_ev_remote_name ev; @@ -2376,64 +2382,64 @@ int mgmt_remote_name(u16 index, bdaddr_t *bdaddr, u8 *name) bacpy(&ev.bdaddr, bdaddr); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - return mgmt_event(MGMT_EV_REMOTE_NAME, index, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_REMOTE_NAME, hdev, &ev, sizeof(ev), NULL); } -int mgmt_inquiry_failed(u16 index, u8 status) +int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; int err; - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, index); + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev->id); if (!cmd) return -ENOENT; - err = cmd_status(cmd->sk, index, cmd->opcode, status); + err = cmd_status(cmd->sk, hdev->id, cmd->opcode, status); mgmt_pending_remove(cmd); return err; } -int mgmt_discovering(u16 index, u8 discovering) +int mgmt_discovering(struct hci_dev *hdev, u8 discovering) { struct pending_cmd *cmd; if (discovering) - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, index); + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev->id); else - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, index); + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev->id); if (cmd != NULL) { - cmd_complete(cmd->sk, index, cmd->opcode, NULL, 0); + cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0); mgmt_pending_remove(cmd); } - return mgmt_event(MGMT_EV_DISCOVERING, index, &discovering, + return mgmt_event(MGMT_EV_DISCOVERING, hdev, &discovering, sizeof(discovering), NULL); } -int mgmt_device_blocked(u16 index, bdaddr_t *bdaddr) +int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct pending_cmd *cmd; struct mgmt_ev_device_blocked ev; - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, index); + cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev->id); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_DEVICE_BLOCKED, index, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } -int mgmt_device_unblocked(u16 index, bdaddr_t *bdaddr) +int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr) { struct pending_cmd *cmd; struct mgmt_ev_device_unblocked ev; - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, index); + cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev->id); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, index, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); + return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } -- cgit v1.2.3 From 2e58ef3e11d0775795345a20185b5a7c4bdae194 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Nov 2011 20:40:15 +0200 Subject: Bluetooth: Move pending management command list into struct hci_dev This patch moves the pending management command list (previously global to mgmt.c) into struct hci_dev. This makes it possible to do proper locking when accessing it (through the existing hci_dev locks) and thereby avoid race conditions. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_core.c | 6 +++ net/bluetooth/mgmt.c | 79 ++++++++++++++++++++-------------------- 3 files changed, 47 insertions(+), 40 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0f35a3900e16..0a5a05d9109c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -217,6 +217,8 @@ struct hci_dev { __u16 init_last_cmd; + struct list_head mgmt_pending; + struct inquiry_cache inq_cache; struct hci_conn_hash conn_hash; struct list_head blacklist; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e4b5c6345095..e5cf01396773 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1481,6 +1481,8 @@ int hci_register_dev(struct hci_dev *hdev) hci_conn_hash_init(hdev); + INIT_LIST_HEAD(&hdev->mgmt_pending); + INIT_LIST_HEAD(&hdev->blacklist); INIT_LIST_HEAD(&hdev->uuids); @@ -1562,6 +1564,10 @@ void hci_unregister_dev(struct hci_dev *hdev) !test_bit(HCI_SETUP, &hdev->flags)) mgmt_index_removed(hdev); + /* mgmt_index_removed should take care of emptying the + * pending list */ + BUG_ON(!list_empty(&hdev->mgmt_pending)); + hci_notify(hdev, HCI_DEV_UNREG); if (hdev->rfkill) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 2ca7b4427e34..be198f382ed8 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -43,8 +43,6 @@ struct pending_cmd { void *user_data; }; -static LIST_HEAD(cmd_list); - static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { struct sk_buff *skb; @@ -227,7 +225,8 @@ static void mgmt_pending_free(struct pending_cmd *cmd) } static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, - u16 index, void *data, u16 len) + struct hci_dev *hdev, + void *data, u16 len) { struct pending_cmd *cmd; @@ -236,7 +235,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, return NULL; cmd->opcode = opcode; - cmd->index = index; + cmd->index = hdev->id; cmd->param = kmalloc(len, GFP_ATOMIC); if (!cmd->param) { @@ -250,7 +249,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, cmd->sk = sk; sock_hold(sk); - list_add(&cmd->list, &cmd_list); + list_add(&cmd->list, &hdev->mgmt_pending); return cmd; } @@ -261,7 +260,7 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, { struct list_head *p, *n; - list_for_each_safe(p, n, &cmd_list) { + list_for_each_safe(p, n, &hdev->mgmt_pending) { struct pending_cmd *cmd; cmd = list_entry(p, struct pending_cmd, list); @@ -276,15 +275,15 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, } } -static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) +static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) { struct pending_cmd *cmd; - list_for_each_entry(cmd, &cmd_list, list) { + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode != opcode) continue; - if (index >= 0 && cmd->index != index) + if (hdev && cmd->index != hdev->id) continue; return cmd; @@ -325,12 +324,12 @@ static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) { + if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) { err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY); goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -376,8 +375,8 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY); goto failed; } @@ -388,7 +387,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -442,8 +441,8 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY); goto failed; } @@ -453,7 +452,7 @@ static int set_connectable(struct sock *sk, u16 index, unsigned char *data, goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1038,7 +1037,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) goto failed; } - if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) { + if (mgmt_pending_find(MGMT_OP_DISCONNECT, hdev)) { err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY); goto failed; } @@ -1052,7 +1051,7 @@ static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1143,7 +1142,7 @@ static int send_pin_code_neg_reply(struct sock *sk, u16 index, struct pending_cmd *cmd; int err; - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp, + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, sizeof(*cp)); if (!cmd) return -ENOMEM; @@ -1204,7 +1203,7 @@ static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1297,7 +1296,7 @@ static inline struct pending_cmd *find_pairing(struct hci_conn *conn) struct hci_dev *hdev = conn->hdev; struct pending_cmd *cmd; - list_for_each_entry(cmd, &cmd_list, list) { + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; @@ -1396,7 +1395,7 @@ static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len); if (!cmd) { err = -ENOMEM; hci_conn_put(conn); @@ -1458,7 +1457,7 @@ static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, goto failed; } - cmd = mgmt_pending_add(sk, mgmt_op, index, data, len); + cmd = mgmt_pending_add(sk, mgmt_op, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1495,7 +1494,7 @@ static int set_local_name(struct sock *sk, u16 index, unsigned char *data, hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len); + cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); if (!cmd) { err = -ENOMEM; goto failed; @@ -1541,12 +1540,12 @@ static int read_local_oob_data(struct sock *sk, u16 index) goto unlock; } - if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, index)) { + if (mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA, EBUSY); goto unlock; } - cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, index, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto unlock; @@ -1650,7 +1649,7 @@ static int start_discovery(struct sock *sk, u16 index) goto failed; } - cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, index, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_START_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto failed; @@ -1681,7 +1680,7 @@ static int stop_discovery(struct sock *sk, u16 index) hci_dev_lock_bh(hdev); - cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, index, NULL, 0); + cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, NULL, 0); if (!cmd) { err = -ENOMEM; goto failed; @@ -2147,7 +2146,7 @@ int mgmt_disconnect_failed(struct hci_dev *hdev) struct pending_cmd *cmd; int err; - cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_DISCONNECT, hdev); if (!cmd) return -ENOENT; @@ -2188,7 +2187,7 @@ int mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, struct mgmt_rp_pin_code_reply rp; int err; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); if (!cmd) return -ENOENT; @@ -2210,7 +2209,7 @@ int mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, struct mgmt_rp_pin_code_reply rp; int err; - cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); if (!cmd) return -ENOENT; @@ -2247,7 +2246,7 @@ static int confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, struct mgmt_rp_user_confirm_reply rp; int err; - cmd = mgmt_pending_find(opcode, hdev->id); + cmd = mgmt_pending_find(opcode, hdev); if (!cmd) return -ENOENT; @@ -2293,7 +2292,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) memset(&ev, 0, sizeof(ev)); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); - cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); if (!cmd) goto send_event; @@ -2330,7 +2329,7 @@ int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, BT_DBG("%s status %u", hdev->name, status); - cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); if (!cmd) return -ENOENT; @@ -2390,7 +2389,7 @@ int mgmt_inquiry_failed(struct hci_dev *hdev, u8 status) struct pending_cmd *cmd; int err; - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); if (!cmd) return -ENOENT; @@ -2405,9 +2404,9 @@ int mgmt_discovering(struct hci_dev *hdev, u8 discovering) struct pending_cmd *cmd; if (discovering) - cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_START_DISCOVERY, hdev); else - cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_STOP_DISCOVERY, hdev); if (cmd != NULL) { cmd_complete(cmd->sk, hdev->id, cmd->opcode, NULL, 0); @@ -2423,7 +2422,7 @@ int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr) struct pending_cmd *cmd; struct mgmt_ev_device_blocked ev; - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); bacpy(&ev.bdaddr, bdaddr); @@ -2436,7 +2435,7 @@ int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr) struct pending_cmd *cmd; struct mgmt_ev_device_unblocked ev; - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev->id); + cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); bacpy(&ev.bdaddr, bdaddr); -- cgit v1.2.3 From 56e5cb86eb377970825486a5861f5926d65e64c1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Nov 2011 20:40:16 +0200 Subject: Bluetooth: Add missing hci_dev locking when calling mgmt functions Now that the pending commands are within struct hci_dev we can properly control access to them throught the hci_dev locking mechanism. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 12 ++++++++++-- net/bluetooth/hci_event.c | 45 ++++++++++++++++++++++++++++++++++++++++----- net/bluetooth/mgmt.c | 13 +++++++------ 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e5cf01396773..f87bf242539e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -549,8 +549,11 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - if (!test_bit(HCI_SETUP, &hdev->flags)) + if (!test_bit(HCI_SETUP, &hdev->flags)) { + hci_dev_lock_bh(hdev); mgmt_powered(hdev, 1); + hci_dev_unlock_bh(hdev); + } } else { /* Init failed, cleanup */ tasklet_kill(&hdev->rx_task); @@ -642,7 +645,9 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); + hci_dev_lock_bh(hdev); mgmt_powered(hdev, 0); + hci_dev_unlock_bh(hdev); /* Clear flags */ hdev->flags = 0; @@ -1561,8 +1566,11 @@ void hci_unregister_dev(struct hci_dev *hdev) kfree_skb(hdev->reassembly[i]); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->flags)) + !test_bit(HCI_SETUP, &hdev->flags)) { + hci_dev_lock_bh(hdev); mgmt_index_removed(hdev); + hci_dev_unlock_bh(hdev); + } /* mgmt_index_removed should take care of emptying the * pending list */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8303f8fa1821..a89cf1f24e47 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -60,7 +60,9 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_INQUIRY, &hdev->flags); + hci_dev_lock(hdev); mgmt_discovering(hdev, 0); + hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); @@ -201,13 +203,15 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) if (!sent) return; + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_set_local_name_complete(hdev, sent, status); - if (status) - return; + if (status == 0) + memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); - memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); + hci_dev_unlock(hdev); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -282,6 +286,8 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) param = *((__u8 *) sent); + hci_dev_lock(hdev); + if (status != 0) { mgmt_write_scan_failed(hdev, param, status); hdev->discov_timeout = 0; @@ -311,6 +317,7 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) mgmt_connectable(hdev, 0); done: + hci_dev_unlock(hdev); hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); } @@ -834,19 +841,24 @@ static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_pin_code_reply_complete(hdev, &rp->bdaddr, rp->status); if (rp->status != 0) - return; + goto unlock; cp = hci_sent_cmd_data(hdev, HCI_OP_PIN_CODE_REPLY); if (!cp) - return; + goto unlock; conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (conn) conn->pin_length = cp->pin_len; + +unlock: + hci_dev_unlock(hdev); } static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -855,10 +867,15 @@ static void hci_cc_pin_code_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_pin_code_neg_reply_complete(hdev, &rp->bdaddr, rp->status); + + hci_dev_unlock(hdev); } + static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) { @@ -885,9 +902,13 @@ static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_user_confirm_reply_complete(hdev, &rp->bdaddr, rp->status); + + hci_dev_unlock(hdev); } static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, @@ -897,9 +918,13 @@ static void hci_cc_user_confirm_neg_reply(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); + if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_user_confirm_neg_reply_complete(hdev, &rp->bdaddr, rp->status); + + hci_dev_unlock(hdev); } static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, @@ -909,8 +934,10 @@ static void hci_cc_read_local_oob_data_reply(struct hci_dev *hdev, BT_DBG("%s status 0x%x", hdev->name, rp->status); + hci_dev_lock(hdev); mgmt_read_local_oob_data_reply_complete(hdev, rp->hash, rp->randomizer, rp->status); + hci_dev_unlock(hdev); } static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, @@ -985,14 +1012,18 @@ static inline void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) if (status) { hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); + hci_dev_lock(hdev); if (test_bit(HCI_MGMT, &hdev->flags)) mgmt_inquiry_failed(hdev, status); + hci_dev_unlock(hdev); return; } set_bit(HCI_INQUIRY, &hdev->flags); + hci_dev_lock(hdev); mgmt_discovering(hdev, 1); + hci_dev_unlock(hdev); } static inline void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) @@ -1378,7 +1409,9 @@ static inline void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff if (!test_and_clear_bit(HCI_INQUIRY, &hdev->flags)) return; + hci_dev_lock(hdev); mgmt_discovering(hdev, 0); + hci_dev_unlock(hdev); } static inline void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) @@ -1572,7 +1605,9 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff BT_DBG("%s status %d", hdev->name, ev->status); if (ev->status) { + hci_dev_lock(hdev); mgmt_disconnect_failed(hdev); + hci_dev_unlock(hdev); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index be198f382ed8..be4c3d03d808 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1335,16 +1335,19 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status) static void pairing_complete_cb(struct hci_conn *conn, u8 status) { struct pending_cmd *cmd; + struct hci_dev *hdev = conn->hdev; BT_DBG("status %u", status); + hci_dev_lock_bh(hdev); + cmd = find_pairing(conn); - if (!cmd) { + if (!cmd) BT_DBG("Unable to find a pending command"); - return; - } + else + pairing_complete(cmd, status); - pairing_complete(cmd, status); + hci_dev_unlock_bh(hdev); } static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) @@ -2302,9 +2305,7 @@ int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) goto failed; } - hci_dev_lock_bh(hdev); update_eir(hdev); - hci_dev_unlock_bh(hdev); err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, &ev, sizeof(ev)); -- cgit v1.2.3 From e0f9309f371096b82ad35aa2c27d7f848f37e696 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Nov 2011 01:44:22 +0200 Subject: Bluetooth: Fix cancel_delayed_work_sync usage with locks The cancel_delayed_work_sync function should not be used if we hold any locks. Luckily all places where this is the case it is also safe to use the non-sync version. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/hci_core.c | 6 +++--- net/bluetooth/mgmt.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f87bf242539e..fb3feeb185d7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -599,12 +599,12 @@ static int hci_dev_do_close(struct hci_dev *hdev) tasklet_kill(&hdev->tx_task); if (hdev->discov_timeout > 0) { - cancel_delayed_work_sync(&hdev->discov_off); + cancel_delayed_work(&hdev->discov_off); hdev->discov_timeout = 0; } if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->flags)) - cancel_delayed_work_sync(&hdev->power_off); + cancel_delayed_work(&hdev->power_off); hci_dev_lock_bh(hdev); inquiry_cache_flush(hdev); @@ -828,7 +828,7 @@ int hci_get_dev_list(void __user *arg) read_lock_bh(&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_sync(&hdev->power_off); + cancel_delayed_work(&hdev->power_off); if (!test_bit(HCI_MGMT, &hdev->flags)) set_bit(HCI_PAIRABLE, &hdev->flags); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index be4c3d03d808..263fa2745d48 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -149,7 +149,7 @@ static int read_index_list(struct sock *sk) i = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_and_clear_bit(HCI_AUTO_OFF, &d->flags)) - cancel_delayed_work_sync(&d->power_off); + cancel_delayed_work(&d->power_off); if (test_bit(HCI_SETUP, &d->flags)) continue; @@ -398,7 +398,7 @@ static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, if (cp->val) scan |= SCAN_INQUIRY; else - cancel_delayed_work_sync(&hdev->discov_off); + cancel_delayed_work(&hdev->discov_off); err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) -- cgit v1.2.3 From fc2f4b13d8c91713efb972be42566f7f3625f5ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Nov 2011 13:58:56 +0200 Subject: Bluetooth: Fix consistency with u16 integer type in mgmt pending_cmd For consistency the integer type should be u16 and not __u16. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 263fa2745d48..a849428ffbcb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -36,7 +36,7 @@ struct pending_cmd { struct list_head list; - __u16 opcode; + u16 opcode; int index; void *param; struct sock *sk; -- cgit v1.2.3 From 2aeabcbedd51aef94b61d05b57246d1db4984453 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Nov 2011 13:58:57 +0200 Subject: Bluetooth: Remove redundant hci_dev comparisons in mgmt lookups Now that pending commands are hci_dev specific there's no need to check whether a command matches hci_dev when iterating through them. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo F. Padovan --- net/bluetooth/mgmt.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a849428ffbcb..a6720c6a4d2c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -268,9 +268,6 @@ static void mgmt_pending_foreach(u16 opcode, struct hci_dev *hdev, if (opcode > 0 && cmd->opcode != opcode) continue; - if (hdev && cmd->index != hdev->id) - continue; - cb(cmd, data); } } @@ -280,13 +277,8 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) struct pending_cmd *cmd; list_for_each_entry(cmd, &hdev->mgmt_pending, list) { - if (cmd->opcode != opcode) - continue; - - if (hdev && cmd->index != hdev->id) - continue; - - return cmd; + if (cmd->opcode == opcode) + return cmd; } return NULL; @@ -1300,9 +1292,6 @@ static inline struct pending_cmd *find_pairing(struct hci_conn *conn) if (cmd->opcode != MGMT_OP_PAIR_DEVICE) continue; - if (cmd->index != hdev->id) - continue; - if (cmd->user_data != conn) continue; -- cgit v1.2.3