From bed71748346ae0807c7f7a2913965508dbd61403 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 30 Jan 2013 11:50:56 -0300 Subject: Bluetooth: Rename hci_acl_disconn As hci_acl_disconn function basically sends the HCI Disconnect Command and it is used to disconnect ACL, SCO and LE links, renaming it to hci_disconnect is more suitable. Signed-off-by: Andre Guedes Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_event.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 4925a02ae7e..b9f90169940 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -117,7 +117,7 @@ static void hci_acl_create_connection_cancel(struct hci_conn *conn) hci_send_cmd(conn->hdev, HCI_OP_CREATE_CONN_CANCEL, sizeof(cp), &cp); } -void hci_acl_disconn(struct hci_conn *conn, __u8 reason) +void hci_disconnect(struct hci_conn *conn, __u8 reason) { struct hci_cp_disconnect cp; @@ -253,7 +253,7 @@ static void hci_conn_disconnect(struct hci_conn *conn) hci_amp_disconn(conn, reason); break; default: - hci_acl_disconn(conn, reason); + hci_disconnect(conn, reason); break; } } diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 60793e7b768..4cb46c24a74 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2398,7 +2398,7 @@ static void hci_link_tx_to(struct hci_dev *hdev, __u8 type) if (c->type == type && c->sent) { BT_ERR("%s killing stalled connection %pMR", hdev->name, &c->dst); - hci_acl_disconn(c, HCI_ERROR_REMOTE_USER_TERM); + hci_disconnect(c, HCI_ERROR_REMOTE_USER_TERM); } } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 477726a6351..5892e54835a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2399,7 +2399,7 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (ev->status && conn->state == BT_CONNECTED) { - hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE); + hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_put(conn); goto unlock; } @@ -3472,7 +3472,7 @@ static void hci_key_refresh_complete_evt(struct hci_dev *hdev, clear_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); if (ev->status && conn->state == BT_CONNECTED) { - hci_acl_disconn(conn, HCI_ERROR_AUTH_FAILURE); + hci_disconnect(conn, HCI_ERROR_AUTH_FAILURE); hci_conn_put(conn); goto unlock; } -- cgit v1.2.3 From fd86c9becc1154ee5643caafedf7cbdf8241c176 Mon Sep 17 00:00:00 2001 From: Karl Relton Date: Wed, 20 Feb 2013 18:16:19 +0000 Subject: Bluetooth: Make hidp_get_raw_report abort if the session is terminating After linux 3.2 the hid_destroy_device call in hidp_session cleaning up invokes a hook to the power_supply code which in turn tries to read the battery capacity. This read will trigger a call to hidp_get_raw_report which is bound to fail because the device is being taken away - so rather than wait for the 5 second timeout failure this changes enables it to fail straight away. Signed-off-by: Karl Relton Reviewed-by: David Herrmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hidp/core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c index a7352ff3fd1..2342327f333 100644 --- a/net/bluetooth/hidp/core.c +++ b/net/bluetooth/hidp/core.c @@ -311,6 +311,9 @@ static int hidp_get_raw_report(struct hid_device *hid, int numbered_reports = hid->report_enum[report_type].numbered; int ret; + if (atomic_read(&session->terminate)) + return -EIO; + switch (report_type) { case HID_FEATURE_REPORT: report_type = HIDP_TRANS_GET_REPORT | HIDP_DATA_RTYPE_FEATURE; @@ -722,6 +725,7 @@ static int hidp_session(void *arg) set_current_state(TASK_INTERRUPTIBLE); } set_current_state(TASK_RUNNING); + atomic_inc(&session->terminate); remove_wait_queue(sk_sleep(intr_sk), &intr_wait); remove_wait_queue(sk_sleep(ctrl_sk), &ctrl_wait); -- cgit v1.2.3 From 5e9d7f868f04106139a58212b860dcdc268ad3af Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 24 Feb 2013 19:36:51 +0100 Subject: Bluetooth: discard bt_sock_unregister() errors After we successfully registered a socket via bt_sock_register() there is no reason to ever check the return code of bt_sock_unregister(). If bt_sock_unregister() fails, it means the socket _is_ already unregistered so we have what we want, don't we? Also, to get bt_sock_unregister() to fail, another part of the kernel has to unregister _our_ socket. This is sooo _wrong_ that it will break way earlier than when we unregister our socket. Signed-off-by: David Herrmann Signed-off-by: Gustavo Padovan --- net/bluetooth/bnep/sock.c | 4 +--- net/bluetooth/cmtp/sock.c | 4 +--- net/bluetooth/hci_sock.c | 4 +--- net/bluetooth/hidp/sock.c | 4 +--- net/bluetooth/l2cap_sock.c | 4 +--- net/bluetooth/rfcomm/sock.c | 3 +-- net/bluetooth/sco.c | 3 +-- 7 files changed, 7 insertions(+), 19 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/bnep/sock.c b/net/bluetooth/bnep/sock.c index e7154a58465..5b1c04e2882 100644 --- a/net/bluetooth/bnep/sock.c +++ b/net/bluetooth/bnep/sock.c @@ -253,8 +253,6 @@ error: void __exit bnep_sock_cleanup(void) { bt_procfs_cleanup(&init_net, "bnep"); - if (bt_sock_unregister(BTPROTO_BNEP) < 0) - BT_ERR("Can't unregister BNEP socket"); - + bt_sock_unregister(BTPROTO_BNEP); proto_unregister(&bnep_proto); } diff --git a/net/bluetooth/cmtp/sock.c b/net/bluetooth/cmtp/sock.c index 1c57482112b..58d9edebab4 100644 --- a/net/bluetooth/cmtp/sock.c +++ b/net/bluetooth/cmtp/sock.c @@ -264,8 +264,6 @@ error: void cmtp_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "cmtp"); - if (bt_sock_unregister(BTPROTO_CMTP) < 0) - BT_ERR("Can't unregister CMTP socket"); - + bt_sock_unregister(BTPROTO_CMTP); proto_unregister(&cmtp_proto); } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 6a93614f2c4..ec044d31f79 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -1121,8 +1121,6 @@ error: void hci_sock_cleanup(void) { bt_procfs_cleanup(&init_net, "hci"); - if (bt_sock_unregister(BTPROTO_HCI) < 0) - BT_ERR("HCI socket unregistration failed"); - + bt_sock_unregister(BTPROTO_HCI); proto_unregister(&hci_sk_proto); } diff --git a/net/bluetooth/hidp/sock.c b/net/bluetooth/hidp/sock.c index 82a829d90b0..5d0f1ca0a31 100644 --- a/net/bluetooth/hidp/sock.c +++ b/net/bluetooth/hidp/sock.c @@ -304,8 +304,6 @@ error: void __exit hidp_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "hidp"); - if (bt_sock_unregister(BTPROTO_HIDP) < 0) - BT_ERR("Can't unregister HIDP socket"); - + bt_sock_unregister(BTPROTO_HIDP); proto_unregister(&hidp_proto); } diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 1bcfb8422fd..7f9704993b7 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1312,8 +1312,6 @@ error: void l2cap_cleanup_sockets(void) { bt_procfs_cleanup(&init_net, "l2cap"); - if (bt_sock_unregister(BTPROTO_L2CAP) < 0) - BT_ERR("L2CAP socket unregistration failed"); - + bt_sock_unregister(BTPROTO_L2CAP); proto_unregister(&l2cap_proto); } diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index c23bae86263..3786ddc4515 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -1065,8 +1065,7 @@ void __exit rfcomm_cleanup_sockets(void) debugfs_remove(rfcomm_sock_debugfs); - if (bt_sock_unregister(BTPROTO_RFCOMM) < 0) - BT_ERR("RFCOMM socket layer unregistration failed"); + bt_sock_unregister(BTPROTO_RFCOMM); proto_unregister(&rfcomm_proto); } diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 79d87d8d4f5..0a3aeb7e0aa 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -1111,8 +1111,7 @@ void __exit sco_exit(void) debugfs_remove(sco_debugfs); - if (bt_sock_unregister(BTPROTO_SCO) < 0) - BT_ERR("SCO socket unregistration failed"); + bt_sock_unregister(BTPROTO_SCO); proto_unregister(&sco_proto); } -- cgit v1.2.3 From be9f97f04565a6c438b7521ad679870d25645475 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Sun, 24 Feb 2013 19:36:52 +0100 Subject: Bluetooth: change bt_sock_unregister() to return void There is no reason a caller ever wants to check the return type of this call. _Iff_ a user successfully called bt_sock_register(), they're allowed to call bt_sock_unregister(). All other calls in the kernel (device_del, device_unregister, kfree(), ..) that are logically equivalent return void. Lets not make callers think they have to check the return type of this call and instead simply return void. We guarantee that after bt_sock_unregister() is called, the socket type _is_ unregistered. If that is not what the caller wants, they're using the wrong function, anyway. Signed-off-by: David Herrmann Signed-off-by: Gustavo Padovan --- net/bluetooth/af_bluetooth.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index d3ee69b35a7..81598e588f7 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -92,23 +92,14 @@ int bt_sock_register(int proto, const struct net_proto_family *ops) } EXPORT_SYMBOL(bt_sock_register); -int bt_sock_unregister(int proto) +void bt_sock_unregister(int proto) { - int err = 0; - if (proto < 0 || proto >= BT_MAX_PROTO) - return -EINVAL; + return; write_lock(&bt_proto_lock); - - if (!bt_proto[proto]) - err = -ENOENT; - else - bt_proto[proto] = NULL; - + bt_proto[proto] = NULL; write_unlock(&bt_proto_lock); - - return err; } EXPORT_SYMBOL(bt_sock_unregister); -- cgit v1.2.3 From fea7b02fbf73adb2e746f00ed279a782de7e74e4 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:53 +0000 Subject: Bluetooth: Avoid rfcomm_session_timeout using freed session Use del_timer_sync() instead of del_timer() as this ensures that rfcomm_session_timeout() is not running on a different CPU when rfcomm_session_put() is called. This avoids a race condition on SMP systems because potentially rfcomm_session_timeout() could reuse the freed RFCOMM session structure caused by the execution of rfcomm_session_put(). Note that this modification makes the reason for the RFCOMM session refcnt mechanism redundant. Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index b23e2713fea..d301fbbe209 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -257,7 +257,7 @@ static void rfcomm_session_clear_timer(struct rfcomm_session *s) { BT_DBG("session %p state %ld", s, s->state); - if (del_timer(&s->timer)) + if (del_timer_sync(&s->timer)) rfcomm_session_put(s); } -- cgit v1.2.3 From c06f7d532aa6f78b2847e3b651c0da27fc3296c0 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:54 +0000 Subject: Bluetooth: Check rfcomm session and DLC exists on socket close A race condition exists between near simultaneous asynchronous DLC data channel disconnection requests from the host and remote device. This causes the socket layer to request a socket shutdown at the same time the rfcomm core is processing the disconnect request from the remote device. The socket layer retains a copy of a struct rfcomm_dlc d pointer. The d pointer refers to a copy of a struct rfcomm_session. When the socket layer thread performs a socket shutdown, the thread may wait on a rfcomm lock in rfcomm_dlc_close(). This means that whilst the thread waits, the rfcomm_session and/or rfcomm_dlc structures pointed to by d maybe freed due to rfcomm core handling. Consequently, when the rfcomm lock becomes available and the thread runs, a malfunction could occur as a freed rfcomm_session structure and/or a freed rfcomm_dlc structure will be erroneously accessed. Therefore, after the rfcomm lock is acquired, check that the struct rfcomm_session is still valid by searching the rfcomm session list. If the session is valid then validate the d pointer by searching the rfcomm session list of active DLCs for the rfcomm_dlc structure pointed by d. Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index d301fbbe209..d9e97cf1792 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -493,12 +493,34 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err) int rfcomm_dlc_close(struct rfcomm_dlc *d, int err) { - int r; + int r = 0; + struct rfcomm_dlc *d_list; + struct rfcomm_session *s, *s_list; + + BT_DBG("dlc %p state %ld dlci %d err %d", d, d->state, d->dlci, err); rfcomm_lock(); - r = __rfcomm_dlc_close(d, err); + s = d->session; + if (!s) + goto no_session; + + /* after waiting on the mutex check the session still exists + * then check the dlc still exists + */ + list_for_each_entry(s_list, &session_list, list) { + if (s_list == s) { + list_for_each_entry(d_list, &s->dlcs, list) { + if (d_list == d) { + r = __rfcomm_dlc_close(d, err); + break; + } + } + break; + } + } +no_session: rfcomm_unlock(); return r; } -- cgit v1.2.3 From 8ff52f7d04d9cc31f1e81dcf9a2ba6335ed34905 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:55 +0000 Subject: Bluetooth: Return RFCOMM session ptrs to avoid freed session Unfortunately, the design retains local copies of the s RFCOMM session pointer in various code blocks and this invites the erroneous access to a freed RFCOMM session structure. Therefore, return the RFCOMM session pointer back up the call stack to avoid accessing a freed RFCOMM session structure. When the RFCOMM session is deleted, NULL is passed up the call stack. If active DLCs exist when the rfcomm session is terminating, avoid a memory leak of rfcomm_dlc structures by ensuring that rfcomm_session_close() is used instead of rfcomm_session_del(). Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 106 +++++++++++++++++++++++--------------------- 1 file changed, 56 insertions(+), 50 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index d9e97cf1792..2b5c543638b 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -69,7 +69,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, u8 sec_level, int *err); static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst); -static void rfcomm_session_del(struct rfcomm_session *s); +static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s); /* ---- RFCOMM frame parsing macros ---- */ #define __get_dlci(b) ((b & 0xfc) >> 2) @@ -108,10 +108,12 @@ static void rfcomm_schedule(void) wake_up_process(rfcomm_thread); } -static void rfcomm_session_put(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_session_put(struct rfcomm_session *s) { - if (atomic_dec_and_test(&s->refcnt)) - rfcomm_session_del(s); + if (s && atomic_dec_and_test(&s->refcnt)) + s = rfcomm_session_del(s); + + return s; } /* ---- RFCOMM FCS computation ---- */ @@ -631,7 +633,7 @@ static struct rfcomm_session *rfcomm_session_add(struct socket *sock, int state) return s; } -static void rfcomm_session_del(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s) { int state = s->state; @@ -648,6 +650,8 @@ static void rfcomm_session_del(struct rfcomm_session *s) if (state != BT_LISTEN) module_put(THIS_MODULE); + + return NULL; } static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) @@ -666,7 +670,8 @@ static struct rfcomm_session *rfcomm_session_get(bdaddr_t *src, bdaddr_t *dst) return NULL; } -static void rfcomm_session_close(struct rfcomm_session *s, int err) +static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, + int err) { struct rfcomm_dlc *d; struct list_head *p, *n; @@ -685,7 +690,7 @@ static void rfcomm_session_close(struct rfcomm_session *s, int err) } rfcomm_session_clear_timer(s); - rfcomm_session_put(s); + return rfcomm_session_put(s); } static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, @@ -737,8 +742,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, if (*err == 0 || *err == -EINPROGRESS) return s; - rfcomm_session_del(s); - return NULL; + return rfcomm_session_del(s); failed: sock_release(sock); @@ -1127,7 +1131,7 @@ static void rfcomm_make_uih(struct sk_buff *skb, u8 addr) } /* ---- RFCOMM frame reception ---- */ -static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) +static struct rfcomm_session *rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) { BT_DBG("session %p state %ld dlci %d", s, s->state, dlci); @@ -1136,7 +1140,7 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) struct rfcomm_dlc *d = rfcomm_dlc_get(s, dlci); if (!d) { rfcomm_send_dm(s, dlci); - return 0; + return s; } switch (d->state) { @@ -1172,25 +1176,14 @@ static int rfcomm_recv_ua(struct rfcomm_session *s, u8 dlci) break; case BT_DISCONN: - /* rfcomm_session_put is called later so don't do - * anything here otherwise we will mess up the session - * reference counter: - * - * (a) when we are the initiator dlc_unlink will drive - * the reference counter to 0 (there is no initial put - * after session_add) - * - * (b) when we are not the initiator rfcomm_rx_process - * will explicitly call put to balance the initial hold - * done after session add. - */ + s = rfcomm_session_close(s, ECONNRESET); break; } } - return 0; + return s; } -static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) +static struct rfcomm_session *rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) { int err = 0; @@ -1215,12 +1208,13 @@ static int rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) err = ECONNRESET; s->state = BT_CLOSED; - rfcomm_session_close(s, err); + s = rfcomm_session_close(s, err); } - return 0; + return s; } -static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) +static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s, + u8 dlci) { int err = 0; @@ -1250,10 +1244,9 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci) err = ECONNRESET; s->state = BT_CLOSED; - rfcomm_session_close(s, err); + s = rfcomm_session_close(s, err); } - - return 0; + return s; } void rfcomm_dlc_accept(struct rfcomm_dlc *d) @@ -1674,11 +1667,18 @@ drop: return 0; } -static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) +static struct rfcomm_session *rfcomm_recv_frame(struct rfcomm_session *s, + struct sk_buff *skb) { struct rfcomm_hdr *hdr = (void *) skb->data; u8 type, dlci, fcs; + if (!s) { + /* no session, so free socket data */ + kfree_skb(skb); + return s; + } + dlci = __get_dlci(hdr->addr); type = __get_type(hdr->ctrl); @@ -1689,7 +1689,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) if (__check_fcs(skb->data, type, fcs)) { BT_ERR("bad checksum in packet"); kfree_skb(skb); - return -EILSEQ; + return s; } if (__test_ea(hdr->len)) @@ -1705,22 +1705,23 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) case RFCOMM_DISC: if (__test_pf(hdr->ctrl)) - rfcomm_recv_disc(s, dlci); + s = rfcomm_recv_disc(s, dlci); break; case RFCOMM_UA: if (__test_pf(hdr->ctrl)) - rfcomm_recv_ua(s, dlci); + s = rfcomm_recv_ua(s, dlci); break; case RFCOMM_DM: - rfcomm_recv_dm(s, dlci); + s = rfcomm_recv_dm(s, dlci); break; case RFCOMM_UIH: - if (dlci) - return rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); - + if (dlci) { + rfcomm_recv_data(s, dlci, __test_pf(hdr->ctrl), skb); + return s; + } rfcomm_recv_mcc(s, skb); break; @@ -1729,7 +1730,7 @@ static int rfcomm_recv_frame(struct rfcomm_session *s, struct sk_buff *skb) break; } kfree_skb(skb); - return 0; + return s; } /* ---- Connection and data processing ---- */ @@ -1866,7 +1867,7 @@ static void rfcomm_process_dlcs(struct rfcomm_session *s) } } -static void rfcomm_process_rx(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s) { struct socket *sock = s->sock; struct sock *sk = sock->sk; @@ -1878,17 +1879,20 @@ static void rfcomm_process_rx(struct rfcomm_session *s) while ((skb = skb_dequeue(&sk->sk_receive_queue))) { skb_orphan(skb); if (!skb_linearize(skb)) - rfcomm_recv_frame(s, skb); + s = rfcomm_recv_frame(s, skb); else kfree_skb(skb); } - if (sk->sk_state == BT_CLOSED) { + if (s && (sk->sk_state == BT_CLOSED)) { if (!s->initiator) - rfcomm_session_put(s); + s = rfcomm_session_put(s); - rfcomm_session_close(s, sk->sk_err); + if (s) + s = rfcomm_session_close(s, sk->sk_err); } + + return s; } static void rfcomm_accept_connection(struct rfcomm_session *s) @@ -1925,7 +1929,7 @@ static void rfcomm_accept_connection(struct rfcomm_session *s) sock_release(nsock); } -static void rfcomm_check_connection(struct rfcomm_session *s) +static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s) { struct sock *sk = s->sock->sk; @@ -1944,9 +1948,10 @@ static void rfcomm_check_connection(struct rfcomm_session *s) case BT_CLOSED: s->state = BT_CLOSED; - rfcomm_session_close(s, sk->sk_err); + s = rfcomm_session_close(s, sk->sk_err); break; } + return s; } static void rfcomm_process_sessions(void) @@ -1975,15 +1980,16 @@ static void rfcomm_process_sessions(void) switch (s->state) { case BT_BOUND: - rfcomm_check_connection(s); + s = rfcomm_check_connection(s); break; default: - rfcomm_process_rx(s); + s = rfcomm_process_rx(s); break; } - rfcomm_process_dlcs(s); + if (s) + rfcomm_process_dlcs(s); rfcomm_session_put(s); } -- cgit v1.2.3 From 08c30aca9e698faddebd34f81e1196295f9dc063 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:56 +0000 Subject: Bluetooth: Remove RFCOMM session refcnt Previous commits have improved the handling of the RFCOMM session timer and the RFCOMM session pointers such that freed RFCOMM session structures should no longer be erroneously accessed. The RFCOMM session refcnt now has no purpose and will be deleted by this commit. Note that the RFCOMM session is now deleted as soon as the RFCOMM control channel link is no longer required. This makes the lifetime of the RFCOMM session deterministic and absolute. Previously with the refcnt, there was uncertainty about when the session structure would be deleted because the relative refcnt prevented the session structure from being deleted at will. It was noted that the refcnt could malfunction under very heavy real-time processor loading in embedded SMP environments. This could cause premature RFCOMM session deletion or double session deletion that could result in kernel crashes. Removal of the refcnt prevents this issue. There are 4 connection / disconnection RFCOMM session scenarios: host initiated control link ---> host disconnected control link host initiated ctrl link ---> remote device disconnected ctrl link remote device initiated ctrl link ---> host disconnected ctrl link remote device initiated ctrl link ---> remote device disc'ed ctrl link The control channel connection procedures are independent of the disconnection procedures. Strangely, the RFCOMM session refcnt was applying special treatment so erroneously combining connection and disconnection events. This commit fixes this issue by removing some session code that used the "initiator" member of the session structure that was intended for use with the data channels. Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 43 +++++-------------------------------------- 1 file changed, 5 insertions(+), 38 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 2b5c543638b..75b7bbd8acb 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -108,14 +108,6 @@ static void rfcomm_schedule(void) wake_up_process(rfcomm_thread); } -static struct rfcomm_session *rfcomm_session_put(struct rfcomm_session *s) -{ - if (s && atomic_dec_and_test(&s->refcnt)) - s = rfcomm_session_del(s); - - return s; -} - /* ---- RFCOMM FCS computation ---- */ /* reversed, 8-bit, poly=0x07 */ @@ -251,16 +243,14 @@ static void rfcomm_session_set_timer(struct rfcomm_session *s, long timeout) { BT_DBG("session %p state %ld timeout %ld", s, s->state, timeout); - if (!mod_timer(&s->timer, jiffies + timeout)) - rfcomm_session_hold(s); + mod_timer(&s->timer, jiffies + timeout); } static void rfcomm_session_clear_timer(struct rfcomm_session *s) { BT_DBG("session %p state %ld", s, s->state); - if (del_timer_sync(&s->timer)) - rfcomm_session_put(s); + del_timer_sync(&s->timer); } /* ---- RFCOMM DLCs ---- */ @@ -338,8 +328,6 @@ static void rfcomm_dlc_link(struct rfcomm_session *s, struct rfcomm_dlc *d) { BT_DBG("dlc %p session %p", d, s); - rfcomm_session_hold(s); - rfcomm_session_clear_timer(s); rfcomm_dlc_hold(d); list_add(&d->list, &s->dlcs); @@ -358,8 +346,6 @@ static void rfcomm_dlc_unlink(struct rfcomm_dlc *d) if (list_empty(&s->dlcs)) rfcomm_session_set_timer(s, RFCOMM_IDLE_TIMEOUT); - - rfcomm_session_put(s); } static struct rfcomm_dlc *rfcomm_dlc_get(struct rfcomm_session *s, u8 dlci) @@ -678,8 +664,6 @@ static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, BT_DBG("session %p state %ld err %d", s, s->state, err); - rfcomm_session_hold(s); - s->state = BT_CLOSED; /* Close all dlcs */ @@ -690,7 +674,7 @@ static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, } rfcomm_session_clear_timer(s); - return rfcomm_session_put(s); + return rfcomm_session_del(s); } static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, @@ -1884,13 +1868,8 @@ static struct rfcomm_session *rfcomm_process_rx(struct rfcomm_session *s) kfree_skb(skb); } - if (s && (sk->sk_state == BT_CLOSED)) { - if (!s->initiator) - s = rfcomm_session_put(s); - - if (s) - s = rfcomm_session_close(s, sk->sk_err); - } + if (s && (sk->sk_state == BT_CLOSED)) + s = rfcomm_session_close(s, sk->sk_err); return s; } @@ -1917,8 +1896,6 @@ static void rfcomm_accept_connection(struct rfcomm_session *s) s = rfcomm_session_add(nsock, BT_OPEN); if (s) { - rfcomm_session_hold(s); - /* We should adjust MTU on incoming sessions. * L2CAP MTU minus UIH header and FCS. */ s->mtu = min(l2cap_pi(nsock->sk)->chan->omtu, @@ -1967,7 +1944,6 @@ static void rfcomm_process_sessions(void) if (test_and_clear_bit(RFCOMM_TIMED_OUT, &s->flags)) { s->state = BT_DISCONN; rfcomm_send_disc(s, 0); - rfcomm_session_put(s); continue; } @@ -1976,8 +1952,6 @@ static void rfcomm_process_sessions(void) continue; } - rfcomm_session_hold(s); - switch (s->state) { case BT_BOUND: s = rfcomm_check_connection(s); @@ -1990,8 +1964,6 @@ static void rfcomm_process_sessions(void) if (s) rfcomm_process_dlcs(s); - - rfcomm_session_put(s); } rfcomm_unlock(); @@ -2041,7 +2013,6 @@ static int rfcomm_add_listener(bdaddr_t *ba) if (!s) goto failed; - rfcomm_session_hold(s); return 0; failed: sock_release(sock); @@ -2099,8 +2070,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) if (!s) return; - rfcomm_session_hold(s); - list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); @@ -2132,8 +2101,6 @@ static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt) set_bit(RFCOMM_AUTH_REJECT, &d->flags); } - rfcomm_session_put(s); - rfcomm_schedule(); } -- cgit v1.2.3 From 8e888f2783384ec097bc0c88d9949776f3584ed3 Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:57 +0000 Subject: Bluetooth: Remove redundant call to rfcomm_send_disc In rfcomm_session_del() remove the redundant call to rfcomm_send_disc() because it is not possible for the session to be in BT_CONNECTED state during deletion of the session. Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index 75b7bbd8acb..c7e88761d3b 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -627,9 +627,6 @@ static struct rfcomm_session *rfcomm_session_del(struct rfcomm_session *s) list_del(&s->list); - if (state == BT_CONNECTED) - rfcomm_send_disc(s, 0); - rfcomm_session_clear_timer(s); sock_release(s->sock); kfree(s); -- cgit v1.2.3 From 24fd642ccb24c8b5732d7d7b5e98277507860b2a Mon Sep 17 00:00:00 2001 From: Dean Jenkins Date: Thu, 28 Feb 2013 14:21:58 +0000 Subject: Bluetooth: Remove redundant RFCOMM BT_CLOSED settings rfcomm_session_close() sets the RFCOMM session state to BT_CLOSED. However, in multiple places immediately before the function is called, the RFCOMM session is set to BT_CLOSED. Therefore, remove these unnecessary state settings. Signed-off-by: Dean Jenkins Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index c7e88761d3b..ba93df2af71 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -659,10 +659,10 @@ static struct rfcomm_session *rfcomm_session_close(struct rfcomm_session *s, struct rfcomm_dlc *d; struct list_head *p, *n; - BT_DBG("session %p state %ld err %d", s, s->state, err); - s->state = BT_CLOSED; + BT_DBG("session %p state %ld err %d", s, s->state, err); + /* Close all dlcs */ list_for_each_safe(p, n, &s->dlcs) { d = list_entry(p, struct rfcomm_dlc, list); @@ -1188,7 +1188,6 @@ static struct rfcomm_session *rfcomm_recv_dm(struct rfcomm_session *s, u8 dlci) else err = ECONNRESET; - s->state = BT_CLOSED; s = rfcomm_session_close(s, err); } return s; @@ -1224,7 +1223,6 @@ static struct rfcomm_session *rfcomm_recv_disc(struct rfcomm_session *s, else err = ECONNRESET; - s->state = BT_CLOSED; s = rfcomm_session_close(s, err); } return s; @@ -1921,7 +1919,6 @@ static struct rfcomm_session *rfcomm_check_connection(struct rfcomm_session *s) break; case BT_CLOSED: - s->state = BT_CLOSED; s = rfcomm_session_close(s, sk->sk_err); break; } -- cgit v1.2.3 From 01178cd420e0134ef3fb4da161ba6390c66913bf Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:41 +0200 Subject: Bluetooth: Rename hci_request to hci_req_sync We'll be introducing an async version of hci_request. To make things clear it makes sense to rename the existing API to have a _sync suffix. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4cb46c24a74..551df8a6f98 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -107,9 +107,9 @@ static void hci_req_cancel(struct hci_dev *hdev, int err) } /* Execute request and wait for completion. */ -static int __hci_request(struct hci_dev *hdev, - void (*req)(struct hci_dev *hdev, unsigned long opt), - unsigned long opt, __u32 timeout) +static int __hci_req_sync(struct hci_dev *hdev, + void (*req)(struct hci_dev *hdev, unsigned long opt), + unsigned long opt, __u32 timeout) { DECLARE_WAITQUEUE(wait, current); int err = 0; @@ -150,9 +150,9 @@ static int __hci_request(struct hci_dev *hdev, return err; } -static int hci_request(struct hci_dev *hdev, - void (*req)(struct hci_dev *hdev, unsigned long opt), - unsigned long opt, __u32 timeout) +static int hci_req_sync(struct hci_dev *hdev, + void (*req)(struct hci_dev *hdev, unsigned long opt), + unsigned long opt, __u32 timeout) { int ret; @@ -161,7 +161,7 @@ static int hci_request(struct hci_dev *hdev, /* Serialize all requests */ hci_req_lock(hdev); - ret = __hci_request(hdev, req, opt, timeout); + ret = __hci_req_sync(hdev, req, opt, timeout); hci_req_unlock(hdev); return ret; @@ -556,7 +556,8 @@ int hci_inquiry(void __user *arg) timeo = ir.length * msecs_to_jiffies(2000); if (do_inquiry) { - err = hci_request(hdev, hci_inq_req, (unsigned long)&ir, timeo); + err = hci_req_sync(hdev, hci_inq_req, (unsigned long) &ir, + timeo); if (err < 0) goto done; } @@ -737,7 +738,7 @@ int hci_dev_open(__u16 dev) set_bit(HCI_INIT, &hdev->flags); hdev->init_last_cmd = 0; - ret = __hci_request(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); + ret = __hci_req_sync(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); } @@ -828,7 +829,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) if (!test_bit(HCI_RAW, &hdev->flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); - __hci_request(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); + __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); clear_bit(HCI_INIT, &hdev->flags); } @@ -921,7 +922,7 @@ int hci_dev_reset(__u16 dev) hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; if (!test_bit(HCI_RAW, &hdev->flags)) - ret = __hci_request(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); + ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); done: hci_req_unlock(hdev); @@ -960,8 +961,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) switch (cmd) { case HCISETAUTH: - err = hci_request(hdev, hci_auth_req, dr.dev_opt, - HCI_INIT_TIMEOUT); + err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt, + HCI_INIT_TIMEOUT); break; case HCISETENCRYPT: @@ -972,24 +973,24 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) if (!test_bit(HCI_AUTH, &hdev->flags)) { /* Auth must be enabled first */ - err = hci_request(hdev, hci_auth_req, dr.dev_opt, - HCI_INIT_TIMEOUT); + err = hci_req_sync(hdev, hci_auth_req, dr.dev_opt, + HCI_INIT_TIMEOUT); if (err) break; } - err = hci_request(hdev, hci_encrypt_req, dr.dev_opt, - HCI_INIT_TIMEOUT); + err = hci_req_sync(hdev, hci_encrypt_req, dr.dev_opt, + HCI_INIT_TIMEOUT); break; case HCISETSCAN: - err = hci_request(hdev, hci_scan_req, dr.dev_opt, - HCI_INIT_TIMEOUT); + err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt, + HCI_INIT_TIMEOUT); break; case HCISETLINKPOL: - err = hci_request(hdev, hci_linkpol_req, dr.dev_opt, - HCI_INIT_TIMEOUT); + err = hci_req_sync(hdev, hci_linkpol_req, dr.dev_opt, + HCI_INIT_TIMEOUT); break; case HCISETLINKMODE: @@ -1608,10 +1609,10 @@ static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, hci_req_lock(hdev); - err = __hci_request(hdev, le_scan_param_req, (unsigned long) ¶m, - timeo); + err = __hci_req_sync(hdev, le_scan_param_req, (unsigned long) ¶m, + timeo); if (!err) - err = __hci_request(hdev, le_scan_enable_req, 0, timeo); + err = __hci_req_sync(hdev, le_scan_enable_req, 0, timeo); hci_req_unlock(hdev); -- cgit v1.2.3 From 53cce22dc795e73fb48205e3f584f63f4c71c90c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:42 +0200 Subject: Bluetooth: Fix __hci_req_sync() handling of empty requests If a request callback doesn't send any commands __hci_req_sync() should fail imediately instead of waiting for the inevitable timeout to occur. This is particularly important once we start creating requests with conditional command sending which can potentially result in no commands being sent at all. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 551df8a6f98..9369e010c90 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -122,6 +122,14 @@ static int __hci_req_sync(struct hci_dev *hdev, set_current_state(TASK_INTERRUPTIBLE); req(hdev, opt); + + /* If the request didn't send any commands return immediately */ + if (skb_queue_empty(&hdev->cmd_q) && atomic_read(&hdev->cmd_cnt)) { + hdev->req_status = 0; + remove_wait_queue(&hdev->req_wait_q, &wait); + return err; + } + schedule_timeout(timeout); remove_wait_queue(&hdev->req_wait_q, &wait); -- cgit v1.2.3 From 2177bab507d2715ae3b745f47056eacd38b79fa7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:43 +0200 Subject: Bluetooth: Split HCI init sequence into three stages Having conditional command sending during a request has always been problematic and caused hacks like the hdev->init_last_cmd variable. This patch removes these conditionals and instead splits the init sequence into three stages, each with its own __hci_req_sync() call. This also paves the way to the upcoming asynchronous request support swhich will also benefit by having a simpler implementation if it doesn't need to cater for requests that change on the fly. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 274 +++++++++++++++++++++++++++++++++++++++++++++- net/bluetooth/hci_event.c | 255 +----------------------------------------- 2 files changed, 274 insertions(+), 255 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9369e010c90..6ab38fecf1f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -193,6 +193,9 @@ static void bredr_init(struct hci_dev *hdev) /* Read Local Version */ hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + + /* Read BD Address */ + hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); } static void amp_init(struct hci_dev *hdev) @@ -209,7 +212,7 @@ static void amp_init(struct hci_dev *hdev) hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); } -static void hci_init_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init1_req(struct hci_dev *hdev, unsigned long opt) { struct sk_buff *skb; @@ -246,6 +249,273 @@ static void hci_init_req(struct hci_dev *hdev, unsigned long opt) } } +static void bredr_setup(struct hci_dev *hdev) +{ + struct hci_cp_delete_stored_link_key cp; + __le16 param; + __u8 flt_type; + + /* Read Buffer Size (ACL mtu, max pkt, etc.) */ + hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); + + /* Read Class of Device */ + hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); + + /* Read Local Name */ + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); + + /* Read Voice Setting */ + hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); + + /* Clear Event Filters */ + flt_type = HCI_FLT_CLEAR_ALL; + hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); + + /* Connection accept timeout ~20 secs */ + param = __constant_cpu_to_le16(0x7d00); + hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); + + bacpy(&cp.bdaddr, BDADDR_ANY); + cp.delete_all = 0x01; + hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); +} + +static void le_setup(struct hci_dev *hdev) +{ + /* Read LE Buffer Size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + + /* Read LE Local Supported Features */ + hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); + + /* Read LE Advertising Channel TX Power */ + hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); + + /* Read LE White List Size */ + hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); + + /* Read LE Supported States */ + hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); +} + +static u8 hci_get_inquiry_mode(struct hci_dev *hdev) +{ + if (lmp_ext_inq_capable(hdev)) + return 0x02; + + if (lmp_inq_rssi_capable(hdev)) + return 0x01; + + if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && + hdev->lmp_subver == 0x0757) + return 0x01; + + if (hdev->manufacturer == 15) { + if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) + return 0x01; + if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) + return 0x01; + if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) + return 0x01; + } + + if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && + hdev->lmp_subver == 0x1805) + return 0x01; + + return 0x00; +} + +static void hci_setup_inquiry_mode(struct hci_dev *hdev) +{ + u8 mode; + + mode = hci_get_inquiry_mode(hdev); + + hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); +} + +static void hci_setup_event_mask(struct hci_dev *hdev) +{ + /* The second byte is 0xff instead of 0x9f (two reserved bits + * disabled) since a Broadcom 1.2 dongle doesn't respond to the + * command otherwise. + */ + u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; + + /* CSR 1.1 dongles does not accept any bitfield so don't try to set + * any event mask for pre 1.2 devices. + */ + if (hdev->hci_ver < BLUETOOTH_VER_1_2) + return; + + if (lmp_bredr_capable(hdev)) { + events[4] |= 0x01; /* Flow Specification Complete */ + events[4] |= 0x02; /* Inquiry Result with RSSI */ + events[4] |= 0x04; /* Read Remote Extended Features Complete */ + events[5] |= 0x08; /* Synchronous Connection Complete */ + events[5] |= 0x10; /* Synchronous Connection Changed */ + } + + if (lmp_inq_rssi_capable(hdev)) + events[4] |= 0x02; /* Inquiry Result with RSSI */ + + if (lmp_sniffsubr_capable(hdev)) + events[5] |= 0x20; /* Sniff Subrating */ + + if (lmp_pause_enc_capable(hdev)) + events[5] |= 0x80; /* Encryption Key Refresh Complete */ + + if (lmp_ext_inq_capable(hdev)) + events[5] |= 0x40; /* Extended Inquiry Result */ + + if (lmp_no_flush_capable(hdev)) + events[7] |= 0x01; /* Enhanced Flush Complete */ + + if (lmp_lsto_capable(hdev)) + events[6] |= 0x80; /* Link Supervision Timeout Changed */ + + if (lmp_ssp_capable(hdev)) { + events[6] |= 0x01; /* IO Capability Request */ + events[6] |= 0x02; /* IO Capability Response */ + events[6] |= 0x04; /* User Confirmation Request */ + events[6] |= 0x08; /* User Passkey Request */ + events[6] |= 0x10; /* Remote OOB Data Request */ + events[6] |= 0x20; /* Simple Pairing Complete */ + events[7] |= 0x04; /* User Passkey Notification */ + events[7] |= 0x08; /* Keypress Notification */ + events[7] |= 0x10; /* Remote Host Supported + * Features Notification + */ + } + + if (lmp_le_capable(hdev)) + events[7] |= 0x20; /* LE Meta-Event */ + + hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); + + if (lmp_le_capable(hdev)) { + memset(events, 0, sizeof(events)); + events[0] = 0x1f; + hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK, + sizeof(events), events); + } +} + +static void hci_init2_req(struct hci_dev *hdev, unsigned long opt) +{ + if (lmp_bredr_capable(hdev)) + bredr_setup(hdev); + + if (lmp_le_capable(hdev)) + le_setup(hdev); + + hci_setup_event_mask(hdev); + + if (hdev->hci_ver > BLUETOOTH_VER_1_1) + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); + + if (lmp_ssp_capable(hdev)) { + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + u8 mode = 0x01; + hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, + sizeof(mode), &mode); + } else { + struct hci_cp_write_eir cp; + + memset(hdev->eir, 0, sizeof(hdev->eir)); + memset(&cp, 0, sizeof(cp)); + + hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); + } + } + + if (lmp_inq_rssi_capable(hdev)) + hci_setup_inquiry_mode(hdev); + + if (lmp_inq_tx_pwr_capable(hdev)) + hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); + + if (lmp_ext_feat_capable(hdev)) { + struct hci_cp_read_local_ext_features cp; + + cp.page = 0x01; + hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), + &cp); + } + + if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { + u8 enable = 1; + hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), + &enable); + } +} + +static void hci_setup_link_policy(struct hci_dev *hdev) +{ + struct hci_cp_write_def_link_policy cp; + u16 link_policy = 0; + + if (lmp_rswitch_capable(hdev)) + link_policy |= HCI_LP_RSWITCH; + if (lmp_hold_capable(hdev)) + link_policy |= HCI_LP_HOLD; + if (lmp_sniff_capable(hdev)) + link_policy |= HCI_LP_SNIFF; + if (lmp_park_capable(hdev)) + link_policy |= HCI_LP_PARK; + + cp.policy = cpu_to_le16(link_policy); + hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); +} + +static void hci_set_le_support(struct hci_dev *hdev) +{ + struct hci_cp_write_le_host_supported cp; + + memset(&cp, 0, sizeof(cp)); + + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + cp.le = 0x01; + cp.simul = lmp_le_br_capable(hdev); + } + + if (cp.le != lmp_host_le_capable(hdev)) + hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), + &cp); +} + +static void hci_init3_req(struct hci_dev *hdev, unsigned long opt) +{ + if (hdev->commands[5] & 0x10) + hci_setup_link_policy(hdev); + + if (lmp_le_capable(hdev)) + hci_set_le_support(hdev); +} + +static int __hci_init(struct hci_dev *hdev) +{ + int err; + + err = __hci_req_sync(hdev, hci_init1_req, 0, HCI_INIT_TIMEOUT); + if (err < 0) + return err; + + /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode + * BR/EDR/LE type controllers. AMP controllers only need the + * first stage init. + */ + if (hdev->dev_type != HCI_BREDR) + return 0; + + err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT); + if (err < 0) + return err; + + return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT); +} + static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) { __u8 scan = opt; @@ -746,7 +1016,7 @@ int hci_dev_open(__u16 dev) set_bit(HCI_INIT, &hdev->flags); hdev->init_last_cmd = 0; - ret = __hci_req_sync(hdev, hci_init_req, 0, HCI_INIT_TIMEOUT); + ret = __hci_init(hdev); clear_bit(HCI_INIT, &hdev->flags); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5892e54835a..14e872aa0d2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -472,211 +472,6 @@ static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) } } -static u8 hci_get_inquiry_mode(struct hci_dev *hdev) -{ - if (lmp_ext_inq_capable(hdev)) - return 2; - - if (lmp_inq_rssi_capable(hdev)) - return 1; - - if (hdev->manufacturer == 11 && hdev->hci_rev == 0x00 && - hdev->lmp_subver == 0x0757) - return 1; - - if (hdev->manufacturer == 15) { - if (hdev->hci_rev == 0x03 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x09 && hdev->lmp_subver == 0x6963) - return 1; - if (hdev->hci_rev == 0x00 && hdev->lmp_subver == 0x6965) - return 1; - } - - if (hdev->manufacturer == 31 && hdev->hci_rev == 0x2005 && - hdev->lmp_subver == 0x1805) - return 1; - - return 0; -} - -static void hci_setup_inquiry_mode(struct hci_dev *hdev) -{ - u8 mode; - - mode = hci_get_inquiry_mode(hdev); - - hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); -} - -static void hci_setup_event_mask(struct hci_dev *hdev) -{ - /* The second byte is 0xff instead of 0x9f (two reserved bits - * disabled) since a Broadcom 1.2 dongle doesn't respond to the - * command otherwise */ - u8 events[8] = { 0xff, 0xff, 0xfb, 0xff, 0x00, 0x00, 0x00, 0x00 }; - - /* CSR 1.1 dongles does not accept any bitfield so don't try to set - * any event mask for pre 1.2 devices */ - if (hdev->hci_ver < BLUETOOTH_VER_1_2) - return; - - if (lmp_bredr_capable(hdev)) { - events[4] |= 0x01; /* Flow Specification Complete */ - events[4] |= 0x02; /* Inquiry Result with RSSI */ - events[4] |= 0x04; /* Read Remote Extended Features Complete */ - events[5] |= 0x08; /* Synchronous Connection Complete */ - events[5] |= 0x10; /* Synchronous Connection Changed */ - } - - if (lmp_inq_rssi_capable(hdev)) - events[4] |= 0x02; /* Inquiry Result with RSSI */ - - if (lmp_sniffsubr_capable(hdev)) - events[5] |= 0x20; /* Sniff Subrating */ - - if (lmp_pause_enc_capable(hdev)) - events[5] |= 0x80; /* Encryption Key Refresh Complete */ - - if (lmp_ext_inq_capable(hdev)) - events[5] |= 0x40; /* Extended Inquiry Result */ - - if (lmp_no_flush_capable(hdev)) - events[7] |= 0x01; /* Enhanced Flush Complete */ - - if (lmp_lsto_capable(hdev)) - events[6] |= 0x80; /* Link Supervision Timeout Changed */ - - if (lmp_ssp_capable(hdev)) { - events[6] |= 0x01; /* IO Capability Request */ - events[6] |= 0x02; /* IO Capability Response */ - events[6] |= 0x04; /* User Confirmation Request */ - events[6] |= 0x08; /* User Passkey Request */ - events[6] |= 0x10; /* Remote OOB Data Request */ - events[6] |= 0x20; /* Simple Pairing Complete */ - events[7] |= 0x04; /* User Passkey Notification */ - events[7] |= 0x08; /* Keypress Notification */ - events[7] |= 0x10; /* Remote Host Supported - * Features Notification */ - } - - if (lmp_le_capable(hdev)) - events[7] |= 0x20; /* LE Meta-Event */ - - hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); - - if (lmp_le_capable(hdev)) { - memset(events, 0, sizeof(events)); - events[0] = 0x1f; - hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK, - sizeof(events), events); - } -} - -static void bredr_setup(struct hci_dev *hdev) -{ - struct hci_cp_delete_stored_link_key cp; - __le16 param; - __u8 flt_type; - - /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); - - /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); - - /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); - - /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); - - /* Clear Event Filters */ - flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); - - /* Connection accept timeout ~20 secs */ - param = __constant_cpu_to_le16(0x7d00); - hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); - - bacpy(&cp.bdaddr, BDADDR_ANY); - cp.delete_all = 1; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); -} - -static void le_setup(struct hci_dev *hdev) -{ - /* Read LE Buffer Size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); - - /* Read LE Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); - - /* Read LE Advertising Channel TX Power */ - hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); - - /* Read LE White List Size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); - - /* Read LE Supported States */ - hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); -} - -static void hci_setup(struct hci_dev *hdev) -{ - if (hdev->dev_type != HCI_BREDR) - return; - - /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); - - if (lmp_bredr_capable(hdev)) - bredr_setup(hdev); - - if (lmp_le_capable(hdev)) - le_setup(hdev); - - hci_setup_event_mask(hdev); - - if (hdev->hci_ver > BLUETOOTH_VER_1_1) - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); - - if (lmp_ssp_capable(hdev)) { - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { - u8 mode = 0x01; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, - sizeof(mode), &mode); - } else { - struct hci_cp_write_eir cp; - - memset(hdev->eir, 0, sizeof(hdev->eir)); - memset(&cp, 0, sizeof(cp)); - - hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); - } - } - - if (lmp_inq_rssi_capable(hdev)) - hci_setup_inquiry_mode(hdev); - - if (lmp_inq_tx_pwr_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); - - if (lmp_ext_feat_capable(hdev)) { - struct hci_cp_read_local_ext_features cp; - - cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), - &cp); - } - - if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { - u8 enable = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), - &enable); - } -} - static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_read_local_version *rp = (void *) skb->data; @@ -695,31 +490,10 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name, hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); - if (test_bit(HCI_INIT, &hdev->flags)) - hci_setup(hdev); - done: hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status); } -static void hci_setup_link_policy(struct hci_dev *hdev) -{ - struct hci_cp_write_def_link_policy cp; - u16 link_policy = 0; - - if (lmp_rswitch_capable(hdev)) - link_policy |= HCI_LP_RSWITCH; - if (lmp_hold_capable(hdev)) - link_policy |= HCI_LP_HOLD; - if (lmp_sniff_capable(hdev)) - link_policy |= HCI_LP_SNIFF; - if (lmp_park_capable(hdev)) - link_policy |= HCI_LP_PARK; - - cp.policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); -} - static void hci_cc_read_local_commands(struct hci_dev *hdev, struct sk_buff *skb) { @@ -727,15 +501,9 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (rp->status) - goto done; - - memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); - - if (test_bit(HCI_INIT, &hdev->flags) && (hdev->commands[5] & 0x10)) - hci_setup_link_policy(hdev); + if (!rp->status) + memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); -done: hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); } @@ -795,22 +563,6 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, hdev->features[6], hdev->features[7]); } -static void hci_set_le_support(struct hci_dev *hdev) -{ - struct hci_cp_write_le_host_supported cp; - - memset(&cp, 0, sizeof(cp)); - - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { - cp.le = 1; - cp.simul = lmp_le_br_capable(hdev); - } - - if (cp.le != lmp_host_le_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), - &cp); -} - static void hci_cc_read_local_ext_features(struct hci_dev *hdev, struct sk_buff *skb) { @@ -830,9 +582,6 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, break; } - if (test_bit(HCI_INIT, &hdev->flags) && lmp_le_capable(hdev)) - hci_set_le_support(hdev); - done: hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); } -- cgit v1.2.3 From 3119ae9599e5cdc1b9838563905c500b582ab6a5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:44 +0200 Subject: Bluetooth: Add initial skeleton for asynchronous HCI requests This patch adds the initial definitions and functions for asynchronous HCI requests. Asynchronous requests are essentially a group of HCI commands together with an optional completion callback. The request is tracked through the already existing command queue by having the necessary context information as part of the control buffer of each skb. The only information needed in the skb control buffer is a flag for indicating that the skb is the start of a request as well as the optional complete callback that should be used when the request is complete (this will be found in the last skb of the request). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6ab38fecf1f..94b08aa9a08 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2439,6 +2439,36 @@ static int hci_send_frame(struct sk_buff *skb) return hdev->send(skb); } +void hci_req_init(struct hci_request *req, struct hci_dev *hdev) +{ + skb_queue_head_init(&req->cmd_q); + req->hdev = hdev; +} + +int hci_req_run(struct hci_request *req, hci_req_complete_t complete) +{ + struct hci_dev *hdev = req->hdev; + struct sk_buff *skb; + unsigned long flags; + + BT_DBG("length %u", skb_queue_len(&req->cmd_q)); + + /* Do not allow empty requests */ + if (skb_queue_empty(&req->cmd_q)) + return -EINVAL; + + skb = skb_peek_tail(&req->cmd_q); + bt_cb(skb)->req.complete = complete; + + spin_lock_irqsave(&hdev->cmd_q.lock, flags); + skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q); + spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); + + queue_work(hdev->workqueue, &hdev->cmd_work); + + return 0; +} + /* Send HCI command */ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) { -- cgit v1.2.3 From 1ca3a9d06e87e09d2f852397f1fbf7c442c921b5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:45 +0200 Subject: Bluetooth: Refactor HCI command skb creation This patch moves out the skb creation from hci_send_cmd() into its own prepare_cmd() function. This is essential so the same prepare_cmd() function can be easily reused for skb creation for asynchronous HCI requests. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 94b08aa9a08..d2edcc4643c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2469,20 +2469,16 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) return 0; } -/* Send HCI command */ -int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) +static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, + u32 plen, void *param) { int len = HCI_COMMAND_HDR_SIZE + plen; struct hci_command_hdr *hdr; struct sk_buff *skb; - BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); - skb = bt_skb_alloc(len, GFP_ATOMIC); - if (!skb) { - BT_ERR("%s no memory for command", hdev->name); - return -ENOMEM; - } + if (!skb) + return NULL; hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE); hdr->opcode = cpu_to_le16(opcode); @@ -2496,6 +2492,22 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; + return skb; +} + +/* Send HCI command */ +int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) +{ + struct sk_buff *skb; + + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + + skb = hci_prepare_cmd(hdev, opcode, plen, param); + if (!skb) { + BT_ERR("%s no memory for command", hdev->name); + return -ENOMEM; + } + if (test_bit(HCI_INIT, &hdev->flags)) hdev->init_last_cmd = opcode; -- cgit v1.2.3 From 71c76a170e979d60e01bd093c9b79e3adeb710cc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:46 +0200 Subject: Bluetooth: Introduce new hci_req_add function This function is analogous to hci_send_cmd() but instead of directly queuing the command to hdev->cmd_q it adds it to the local queue of the asynchronous HCI request being build (inside struct hci_request). This is the main function used for building asynchronous requests and there should be one or more calls to it between calls to hci_req_init and hci_req_run. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d2edcc4643c..6e6a9dd8a15 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2517,6 +2517,28 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) return 0; } +/* Queue a command to an asynchronous HCI request */ +int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) +{ + struct hci_dev *hdev = req->hdev; + struct sk_buff *skb; + + BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + + skb = hci_prepare_cmd(hdev, opcode, plen, param); + if (!skb) { + BT_ERR("%s no memory for command", hdev->name); + return -ENOMEM; + } + + if (skb_queue_empty(&req->cmd_q)) + bt_cb(skb)->req.start = true; + + skb_queue_tail(&req->cmd_q, skb); + + return 0; +} + /* Get data from the previously sent command */ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode) { -- cgit v1.2.3 From 11714b3d7acee54eecf85d41c938923a02fdd054 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:47 +0200 Subject: Bluetooth: Fix stand-alone HCI command handling To have a consistent content for hdev->cmd_q all entries need to follow the semantics of asynchronous HCI requests. This means that even single commands need to be dressed as requests by having a request start indicator. This patch adds these indicators to the two places needing it (hci_send_cmd and hci_sock_sendmsg). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 5 +++++ net/bluetooth/hci_sock.c | 5 +++++ 2 files changed, 10 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6e6a9dd8a15..4f8142bdf65 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2511,6 +2511,11 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) if (test_bit(HCI_INIT, &hdev->flags)) hdev->init_last_cmd = opcode; + /* Stand-alone HCI commands must be flaged as + * single-command requests. + */ + bt_cb(skb)->req.start = true; + skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ec044d31f79..aa4354fca77 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -854,6 +854,11 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } else { + /* Stand-alone HCI commands must be flaged as + * single-command requests. + */ + bt_cb(skb)->req.start = true; + skb_queue_tail(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); } -- cgit v1.2.3 From 9238f36a5a5097018b90baa42c473d2f916a46f5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:48 +0200 Subject: Bluetooth: Add request cmd_complete and cmd_status functions This patch introduces functions to process the HCI request state when receiving HCI Command Status or Command Complete events. Some HCI commands, like Inquiry do not result in a Command complete event so special handling is needed for them. Inquiry is a particularly important one since it is the only forseeable "non-cmd_complete" command that will make good use of the request functionality, and its completion is either indicated by an Inquiry Complete event of a successful Command Complete for HCI_Inquiry_Cancel. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++ net/bluetooth/hci_event.c | 7 ++++ 2 files changed, 92 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4f8142bdf65..0ada2ec36e7 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3208,6 +3208,91 @@ static void hci_scodata_packet(struct hci_dev *hdev, struct sk_buff *skb) kfree_skb(skb); } +static bool hci_req_is_complete(struct hci_dev *hdev) +{ + struct sk_buff *skb; + + skb = skb_peek(&hdev->cmd_q); + if (!skb) + return true; + + return bt_cb(skb)->req.start; +} + +void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) +{ + hci_req_complete_t req_complete = NULL; + struct sk_buff *skb; + unsigned long flags; + + BT_DBG("opcode 0x%04x status 0x%02x", opcode, status); + + /* Check that the completed command really matches the last one + * that was sent. + */ + if (!hci_sent_cmd_data(hdev, opcode)) + return; + + /* If the command succeeded and there's still more commands in + * this request the request is not yet complete. + */ + if (!status && !hci_req_is_complete(hdev)) + return; + + /* If this was the last command in a request the complete + * callback would be found in hdev->sent_cmd instead of the + * command queue (hdev->cmd_q). + */ + if (hdev->sent_cmd) { + req_complete = bt_cb(hdev->sent_cmd)->req.complete; + if (req_complete) + goto call_complete; + } + + /* Remove all pending commands belonging to this request */ + spin_lock_irqsave(&hdev->cmd_q.lock, flags); + while ((skb = __skb_dequeue(&hdev->cmd_q))) { + if (bt_cb(skb)->req.start) { + __skb_queue_head(&hdev->cmd_q, skb); + break; + } + + req_complete = bt_cb(skb)->req.complete; + kfree_skb(skb); + } + spin_unlock_irqrestore(&hdev->cmd_q.lock, flags); + +call_complete: + if (req_complete) + req_complete(hdev, status); +} + +void hci_req_cmd_status(struct hci_dev *hdev, u16 opcode, u8 status) +{ + hci_req_complete_t req_complete = NULL; + + BT_DBG("opcode 0x%04x status 0x%02x", opcode, status); + + if (status) { + hci_req_cmd_complete(hdev, opcode, status); + return; + } + + /* No need to handle success status if there are more commands */ + if (!hci_req_is_complete(hdev)) + return; + + if (hdev->sent_cmd) + req_complete = bt_cb(hdev->sent_cmd)->req.complete; + + /* If the request doesn't have a complete callback or there + * are other commands/requests in the hdev queue we consider + * this request as completed. + */ + if (!req_complete || !skb_queue_empty(&hdev->cmd_q)) + hci_req_cmd_complete(hdev, opcode, status); +} + static void hci_rx_work(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, rx_work); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 14e872aa0d2..8b878a3bdf6 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -53,6 +53,7 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); hci_dev_unlock(hdev); + hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); hci_conn_check_pending(hdev); @@ -1692,6 +1693,7 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); + hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); @@ -2254,6 +2256,7 @@ static void hci_qos_setup_complete_evt(struct hci_dev *hdev, static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_cmd_complete *ev = (void *) skb->data; + u8 status = skb->data[sizeof(*ev)]; __u16 opcode; skb_pull(skb, sizeof(*ev)); @@ -2497,6 +2500,8 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); + hci_req_cmd_complete(hdev, ev->opcode, status); + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) @@ -2590,6 +2595,8 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); + hci_req_cmd_status(hdev, ev->opcode, ev->status); + if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); if (!skb_queue_empty(&hdev->cmd_q)) -- cgit v1.2.3 From 42c6b129cd8c2aa5012a78ec39672e7052cc677a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:49 +0200 Subject: Bluetooth: Use async requests internally in hci_req_sync This patch converts the hci_req_sync() procedure to internaly use the asynchronous HCI requests. The hci_req_sync mechanism relies on hci_req_complete() calls from hci_event.c into hci_core.c whenever a HCI command completes. This is very similar to what asynchronous requests do and makes the conversion fairly straight forward by converting hci_req_complete into a request complete callback. By this change hci_req_complete (renamed to hci_req_sync_complete) becomes private to hci_core.c and all calls to it can be removed from hci_event.c. The commands in each hci_req_sync procedure are collected into their own request by passing the hci_request pointer to the request callback (instead of the hci_dev pointer). The one slight exception is the HCI init request which has the special handling of HCI driver specific initialization commands. These commands are run in their own request prior to the "main" init request. One other extra change that this patch must contain is the handling of spontaneous HCI reset complete events that some controllers exhibit. These were previously handled in the hci_req_complete function but the right place for them now becomes the hci_req_cmd_complete function. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 271 ++++++++++++++++++++++++++-------------------- net/bluetooth/hci_event.c | 78 +------------ 2 files changed, 156 insertions(+), 193 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0ada2ec36e7..6218eced153 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -57,36 +57,9 @@ static void hci_notify(struct hci_dev *hdev, int event) /* ---- HCI requests ---- */ -void hci_req_complete(struct hci_dev *hdev, __u16 cmd, int result) +static void hci_req_sync_complete(struct hci_dev *hdev, u8 result) { - BT_DBG("%s command 0x%4.4x result 0x%2.2x", hdev->name, cmd, result); - - /* If this is the init phase check if the completed command matches - * the last init command, and if not just return. - */ - if (test_bit(HCI_INIT, &hdev->flags) && hdev->init_last_cmd != cmd) { - struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; - u16 opcode = __le16_to_cpu(sent->opcode); - struct sk_buff *skb; - - /* Some CSR based controllers generate a spontaneous - * reset complete event during init and any pending - * command will never be completed. In such a case we - * need to resend whatever was the last sent - * command. - */ - - if (cmd != HCI_OP_RESET || opcode == HCI_OP_RESET) - return; - - skb = skb_clone(hdev->sent_cmd, GFP_ATOMIC); - if (skb) { - skb_queue_head(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); - } - - return; - } + BT_DBG("%s result 0x%2.2x", hdev->name, result); if (hdev->req_status == HCI_REQ_PEND) { hdev->req_result = result; @@ -108,26 +81,36 @@ static void hci_req_cancel(struct hci_dev *hdev, int err) /* Execute request and wait for completion. */ static int __hci_req_sync(struct hci_dev *hdev, - void (*req)(struct hci_dev *hdev, unsigned long opt), + void (*func)(struct hci_request *req, + unsigned long opt), unsigned long opt, __u32 timeout) { + struct hci_request req; DECLARE_WAITQUEUE(wait, current); int err = 0; BT_DBG("%s start", hdev->name); + hci_req_init(&req, hdev); + hdev->req_status = HCI_REQ_PEND; add_wait_queue(&hdev->req_wait_q, &wait); set_current_state(TASK_INTERRUPTIBLE); - req(hdev, opt); + func(&req, opt); - /* If the request didn't send any commands return immediately */ - if (skb_queue_empty(&hdev->cmd_q) && atomic_read(&hdev->cmd_cnt)) { + err = hci_req_run(&req, hci_req_sync_complete); + if (err < 0) { hdev->req_status = 0; remove_wait_queue(&hdev->req_wait_q, &wait); - return err; + /* req_run will fail if the request did not add any + * commands to the queue, something that can happen when + * a request with conditionals doesn't trigger any + * commands to be sent. This is normal behavior and + * should not trigger an error return. + */ + return 0; } schedule_timeout(timeout); @@ -159,7 +142,8 @@ static int __hci_req_sync(struct hci_dev *hdev, } static int hci_req_sync(struct hci_dev *hdev, - void (*req)(struct hci_dev *hdev, unsigned long opt), + void (*req)(struct hci_request *req, + unsigned long opt), unsigned long opt, __u32 timeout) { int ret; @@ -175,72 +159,80 @@ static int hci_req_sync(struct hci_dev *hdev, return ret; } -static void hci_reset_req(struct hci_dev *hdev, unsigned long opt) +static void hci_reset_req(struct hci_request *req, unsigned long opt) { - BT_DBG("%s %ld", hdev->name, opt); + BT_DBG("%s %ld", req->hdev->name, opt); /* Reset device */ - set_bit(HCI_RESET, &hdev->flags); - hci_send_cmd(hdev, HCI_OP_RESET, 0, NULL); + set_bit(HCI_RESET, &req->hdev->flags); + hci_req_add(req, HCI_OP_RESET, 0, NULL); } -static void bredr_init(struct hci_dev *hdev) +static void bredr_init(struct hci_request *req) { - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; + req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_PACKET_BASED; /* Read Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_FEATURES, 0, NULL); /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read BD Address */ - hci_send_cmd(hdev, HCI_OP_READ_BD_ADDR, 0, NULL); + hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL); } -static void amp_init(struct hci_dev *hdev) +static void amp_init(struct hci_request *req) { - hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; + req->hdev->flow_ctl_mode = HCI_FLOW_CTL_MODE_BLOCK_BASED; /* Read Local Version */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL); /* Read Local AMP Info */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); /* Read Data Blk size */ - hci_send_cmd(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_READ_DATA_BLOCK_SIZE, 0, NULL); } -static void hci_init1_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init1_req(struct hci_request *req, unsigned long opt) { + struct hci_dev *hdev = req->hdev; + struct hci_request init_req; struct sk_buff *skb; BT_DBG("%s %ld", hdev->name, opt); /* Driver initialization */ + hci_req_init(&init_req, hdev); + /* Special commands */ while ((skb = skb_dequeue(&hdev->driver_init))) { bt_cb(skb)->pkt_type = HCI_COMMAND_PKT; skb->dev = (void *) hdev; - skb_queue_tail(&hdev->cmd_q, skb); - queue_work(hdev->workqueue, &hdev->cmd_work); + if (skb_queue_empty(&init_req.cmd_q)) + bt_cb(skb)->req.start = true; + + skb_queue_tail(&init_req.cmd_q, skb); } skb_queue_purge(&hdev->driver_init); + hci_req_run(&init_req, NULL); + /* Reset */ if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) - hci_reset_req(hdev, 0); + hci_reset_req(req, 0); switch (hdev->dev_type) { case HCI_BREDR: - bredr_init(hdev); + bredr_init(req); break; case HCI_AMP: - amp_init(hdev); + amp_init(req); break; default: @@ -249,53 +241,53 @@ static void hci_init1_req(struct hci_dev *hdev, unsigned long opt) } } -static void bredr_setup(struct hci_dev *hdev) +static void bredr_setup(struct hci_request *req) { struct hci_cp_delete_stored_link_key cp; __le16 param; __u8 flt_type; /* Read Buffer Size (ACL mtu, max pkt, etc.) */ - hci_send_cmd(hdev, HCI_OP_READ_BUFFER_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_READ_BUFFER_SIZE, 0, NULL); /* Read Class of Device */ - hci_send_cmd(hdev, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); + hci_req_add(req, HCI_OP_READ_CLASS_OF_DEV, 0, NULL); /* Read Local Name */ - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_NAME, 0, NULL); /* Read Voice Setting */ - hci_send_cmd(hdev, HCI_OP_READ_VOICE_SETTING, 0, NULL); + hci_req_add(req, HCI_OP_READ_VOICE_SETTING, 0, NULL); /* Clear Event Filters */ flt_type = HCI_FLT_CLEAR_ALL; - hci_send_cmd(hdev, HCI_OP_SET_EVENT_FLT, 1, &flt_type); + hci_req_add(req, HCI_OP_SET_EVENT_FLT, 1, &flt_type); /* Connection accept timeout ~20 secs */ param = __constant_cpu_to_le16(0x7d00); - hci_send_cmd(hdev, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); + hci_req_add(req, HCI_OP_WRITE_CA_TIMEOUT, 2, ¶m); bacpy(&cp.bdaddr, BDADDR_ANY); cp.delete_all = 0x01; - hci_send_cmd(hdev, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); } -static void le_setup(struct hci_dev *hdev) +static void le_setup(struct hci_request *req) { /* Read LE Buffer Size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_BUFFER_SIZE, 0, NULL); /* Read LE Local Supported Features */ - hci_send_cmd(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_LOCAL_FEATURES, 0, NULL); /* Read LE Advertising Channel TX Power */ - hci_send_cmd(hdev, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); /* Read LE White List Size */ - hci_send_cmd(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); /* Read LE Supported States */ - hci_send_cmd(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); + hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); } static u8 hci_get_inquiry_mode(struct hci_dev *hdev) @@ -326,17 +318,19 @@ static u8 hci_get_inquiry_mode(struct hci_dev *hdev) return 0x00; } -static void hci_setup_inquiry_mode(struct hci_dev *hdev) +static void hci_setup_inquiry_mode(struct hci_request *req) { u8 mode; - mode = hci_get_inquiry_mode(hdev); + mode = hci_get_inquiry_mode(req->hdev); - hci_send_cmd(hdev, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); + hci_req_add(req, HCI_OP_WRITE_INQUIRY_MODE, 1, &mode); } -static void hci_setup_event_mask(struct hci_dev *hdev) +static void hci_setup_event_mask(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; + /* The second byte is 0xff instead of 0x9f (two reserved bits * disabled) since a Broadcom 1.2 dongle doesn't respond to the * command otherwise. @@ -392,67 +386,70 @@ static void hci_setup_event_mask(struct hci_dev *hdev) if (lmp_le_capable(hdev)) events[7] |= 0x20; /* LE Meta-Event */ - hci_send_cmd(hdev, HCI_OP_SET_EVENT_MASK, sizeof(events), events); + hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events); if (lmp_le_capable(hdev)) { memset(events, 0, sizeof(events)); events[0] = 0x1f; - hci_send_cmd(hdev, HCI_OP_LE_SET_EVENT_MASK, - sizeof(events), events); + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, + sizeof(events), events); } } -static void hci_init2_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init2_req(struct hci_request *req, unsigned long opt) { + struct hci_dev *hdev = req->hdev; + if (lmp_bredr_capable(hdev)) - bredr_setup(hdev); + bredr_setup(req); if (lmp_le_capable(hdev)) - le_setup(hdev); + le_setup(req); - hci_setup_event_mask(hdev); + hci_setup_event_mask(req); if (hdev->hci_ver > BLUETOOTH_VER_1_1) - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); + hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL); if (lmp_ssp_capable(hdev)) { if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, - sizeof(mode), &mode); + hci_req_add(req, HCI_OP_WRITE_SSP_MODE, + sizeof(mode), &mode); } else { struct hci_cp_write_eir cp; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(&cp, 0, sizeof(cp)); - hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } } if (lmp_inq_rssi_capable(hdev)) - hci_setup_inquiry_mode(hdev); + hci_setup_inquiry_mode(req); if (lmp_inq_tx_pwr_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); + hci_req_add(req, HCI_OP_READ_INQ_RSP_TX_POWER, 0, NULL); if (lmp_ext_feat_capable(hdev)) { struct hci_cp_read_local_ext_features cp; cp.page = 0x01; - hci_send_cmd(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, sizeof(cp), - &cp); + hci_req_add(req, HCI_OP_READ_LOCAL_EXT_FEATURES, + sizeof(cp), &cp); } if (test_bit(HCI_LINK_SECURITY, &hdev->dev_flags)) { u8 enable = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), - &enable); + hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, sizeof(enable), + &enable); } } -static void hci_setup_link_policy(struct hci_dev *hdev) +static void hci_setup_link_policy(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_def_link_policy cp; u16 link_policy = 0; @@ -466,11 +463,12 @@ static void hci_setup_link_policy(struct hci_dev *hdev) link_policy |= HCI_LP_PARK; cp.policy = cpu_to_le16(link_policy); - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, sizeof(cp), &cp); } -static void hci_set_le_support(struct hci_dev *hdev) +static void hci_set_le_support(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_le_host_supported cp; memset(&cp, 0, sizeof(cp)); @@ -481,17 +479,19 @@ static void hci_set_le_support(struct hci_dev *hdev) } if (cp.le != lmp_host_le_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), - &cp); + hci_req_add(req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(cp), + &cp); } -static void hci_init3_req(struct hci_dev *hdev, unsigned long opt) +static void hci_init3_req(struct hci_request *req, unsigned long opt) { + struct hci_dev *hdev = req->hdev; + if (hdev->commands[5] & 0x10) - hci_setup_link_policy(hdev); + hci_setup_link_policy(req); if (lmp_le_capable(hdev)) - hci_set_le_support(hdev); + hci_set_le_support(req); } static int __hci_init(struct hci_dev *hdev) @@ -516,44 +516,44 @@ static int __hci_init(struct hci_dev *hdev) return __hci_req_sync(hdev, hci_init3_req, 0, HCI_INIT_TIMEOUT); } -static void hci_scan_req(struct hci_dev *hdev, unsigned long opt) +static void hci_scan_req(struct hci_request *req, unsigned long opt) { __u8 scan = opt; - BT_DBG("%s %x", hdev->name, scan); + BT_DBG("%s %x", req->hdev->name, scan); /* Inquiry and Page scans */ - hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } -static void hci_auth_req(struct hci_dev *hdev, unsigned long opt) +static void hci_auth_req(struct hci_request *req, unsigned long opt) { __u8 auth = opt; - BT_DBG("%s %x", hdev->name, auth); + BT_DBG("%s %x", req->hdev->name, auth); /* Authentication */ - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); + hci_req_add(req, HCI_OP_WRITE_AUTH_ENABLE, 1, &auth); } -static void hci_encrypt_req(struct hci_dev *hdev, unsigned long opt) +static void hci_encrypt_req(struct hci_request *req, unsigned long opt) { __u8 encrypt = opt; - BT_DBG("%s %x", hdev->name, encrypt); + BT_DBG("%s %x", req->hdev->name, encrypt); /* Encryption */ - hci_send_cmd(hdev, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); + hci_req_add(req, HCI_OP_WRITE_ENCRYPT_MODE, 1, &encrypt); } -static void hci_linkpol_req(struct hci_dev *hdev, unsigned long opt) +static void hci_linkpol_req(struct hci_request *req, unsigned long opt) { __le16 policy = cpu_to_le16(opt); - BT_DBG("%s %x", hdev->name, policy); + BT_DBG("%s %x", req->hdev->name, policy); /* Default link policy */ - hci_send_cmd(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); + hci_req_add(req, HCI_OP_WRITE_DEF_LINK_POLICY, 2, &policy); } /* Get HCI device by index. @@ -790,9 +790,10 @@ static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) return copied; } -static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) +static void hci_inq_req(struct hci_request *req, unsigned long opt) { struct hci_inquiry_req *ir = (struct hci_inquiry_req *) opt; + struct hci_dev *hdev = req->hdev; struct hci_cp_inquiry cp; BT_DBG("%s", hdev->name); @@ -804,7 +805,7 @@ static void hci_inq_req(struct hci_dev *hdev, unsigned long opt) memcpy(&cp.lap, &ir->lap, 3); cp.length = ir->length; cp.num_rsp = ir->num_rsp; - hci_send_cmd(hdev, HCI_OP_INQUIRY, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_INQUIRY, sizeof(cp), &cp); } int hci_inquiry(void __user *arg) @@ -1845,7 +1846,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) return mgmt_device_unblocked(hdev, bdaddr, type); } -static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) +static void le_scan_param_req(struct hci_request *req, unsigned long opt) { struct le_scan_params *param = (struct le_scan_params *) opt; struct hci_cp_le_set_scan_param cp; @@ -1855,10 +1856,10 @@ static void le_scan_param_req(struct hci_dev *hdev, unsigned long opt) cp.interval = cpu_to_le16(param->interval); cp.window = cpu_to_le16(param->window); - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(cp), &cp); } -static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) +static void le_scan_enable_req(struct hci_request *req, unsigned long opt) { struct hci_cp_le_set_scan_enable cp; @@ -1866,7 +1867,7 @@ static void le_scan_enable_req(struct hci_dev *hdev, unsigned long opt) cp.enable = 1; cp.filter_dup = 1; - hci_send_cmd(hdev, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); } static int hci_do_le_scan(struct hci_dev *hdev, u8 type, u16 interval, @@ -3219,6 +3220,28 @@ static bool hci_req_is_complete(struct hci_dev *hdev) return bt_cb(skb)->req.start; } +static void hci_resend_last(struct hci_dev *hdev) +{ + struct hci_command_hdr *sent; + struct sk_buff *skb; + u16 opcode; + + if (!hdev->sent_cmd) + return; + + sent = (void *) hdev->sent_cmd->data; + opcode = __le16_to_cpu(sent->opcode); + if (opcode == HCI_OP_RESET) + return; + + skb = skb_clone(hdev->sent_cmd, GFP_KERNEL); + if (!skb) + return; + + skb_queue_head(&hdev->cmd_q, skb); + queue_work(hdev->workqueue, &hdev->cmd_work); +} + void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) { hci_req_complete_t req_complete = NULL; @@ -3227,11 +3250,21 @@ void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status) BT_DBG("opcode 0x%04x status 0x%02x", opcode, status); - /* Check that the completed command really matches the last one - * that was sent. + /* If the completed command doesn't match the last one that was + * sent we need to do special handling of it. */ - if (!hci_sent_cmd_data(hdev, opcode)) + if (!hci_sent_cmd_data(hdev, opcode)) { + /* Some CSR based controllers generate a spontaneous + * reset complete event during init and any pending + * command will never be completed. In such a case we + * need to resend whatever was the last sent + * command. + */ + if (test_bit(HCI_INIT, &hdev->flags) && opcode == HCI_OP_RESET) + hci_resend_last(hdev); + return; + } /* If the command succeeded and there's still more commands in * this request the request is not yet complete. diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8b878a3bdf6..0dd85a0c05f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -54,7 +54,6 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_unlock(hdev); hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); - hci_req_complete(hdev, HCI_OP_INQUIRY_CANCEL, status); hci_conn_check_pending(hdev); } @@ -184,8 +183,6 @@ static void hci_cc_write_def_link_policy(struct hci_dev *hdev, if (!status) hdev->link_policy = get_unaligned_le16(sent); - - hci_req_complete(hdev, HCI_OP_WRITE_DEF_LINK_POLICY, status); } static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) @@ -196,8 +193,6 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); - hci_req_complete(hdev, HCI_OP_RESET, status); - /* Reset all non-persistent flags */ hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS) | BIT(HCI_PERIODIC_INQ)); @@ -232,8 +227,6 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) if (!status && !test_bit(HCI_INIT, &hdev->flags)) hci_update_ad(hdev); - - hci_req_complete(hdev, HCI_OP_WRITE_LOCAL_NAME, status); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -271,8 +264,6 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) if (test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_auth_enable_complete(hdev, status); - - hci_req_complete(hdev, HCI_OP_WRITE_AUTH_ENABLE, status); } static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) @@ -294,8 +285,6 @@ static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) else clear_bit(HCI_ENCRYPT, &hdev->flags); } - - hci_req_complete(hdev, HCI_OP_WRITE_ENCRYPT_MODE, status); } static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) @@ -344,7 +333,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) done: hci_dev_unlock(hdev); - hci_req_complete(hdev, HCI_OP_WRITE_SCAN_ENABLE, status); } static void hci_cc_read_class_of_dev(struct hci_dev *hdev, struct sk_buff *skb) @@ -441,8 +429,6 @@ static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_HOST_BUFFER_SIZE, status); } static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) @@ -480,7 +466,7 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - goto done; + return; hdev->hci_ver = rp->hci_ver; hdev->hci_rev = __le16_to_cpu(rp->hci_rev); @@ -490,9 +476,6 @@ static void hci_cc_read_local_version(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s manufacturer 0x%4.4x hci ver %d:%d", hdev->name, hdev->manufacturer, hdev->hci_ver, hdev->hci_rev); - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_VERSION, rp->status); } static void hci_cc_read_local_commands(struct hci_dev *hdev, @@ -504,8 +487,6 @@ static void hci_cc_read_local_commands(struct hci_dev *hdev, if (!rp->status) memcpy(hdev->commands, rp->commands, sizeof(hdev->commands)); - - hci_req_complete(hdev, HCI_OP_READ_LOCAL_COMMANDS, rp->status); } static void hci_cc_read_local_features(struct hci_dev *hdev, @@ -572,7 +553,7 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); if (rp->status) - goto done; + return; switch (rp->page) { case 0: @@ -582,9 +563,6 @@ static void hci_cc_read_local_ext_features(struct hci_dev *hdev, memcpy(hdev->host_features, rp->features, 8); break; } - -done: - hci_req_complete(hdev, HCI_OP_READ_LOCAL_EXT_FEATURES, rp->status); } static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, @@ -594,12 +572,8 @@ static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (rp->status) - return; - - hdev->flow_ctl_mode = rp->mode; - - hci_req_complete(hdev, HCI_OP_READ_FLOW_CONTROL_MODE, rp->status); + if (!rp->status) + hdev->flow_ctl_mode = rp->mode; } static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) @@ -636,8 +610,6 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) if (!rp->status) bacpy(&hdev->bdaddr, &rp->bdaddr); - - hci_req_complete(hdev, HCI_OP_READ_BD_ADDR, rp->status); } static void hci_cc_read_data_block_size(struct hci_dev *hdev, @@ -658,8 +630,6 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev, BT_DBG("%s blk mtu %d cnt %d len %d", hdev->name, hdev->block_mtu, hdev->block_cnt, hdev->block_len); - - hci_req_complete(hdev, HCI_OP_READ_DATA_BLOCK_SIZE, rp->status); } static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) @@ -667,8 +637,6 @@ static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_CA_TIMEOUT, status); } static void hci_cc_read_local_amp_info(struct hci_dev *hdev, @@ -692,8 +660,6 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev, 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); - a2mp_rsp: a2mp_send_getinfo_rsp(hdev); } @@ -741,8 +707,6 @@ static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_DELETE_STORED_LINK_KEY, status); } static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) @@ -750,8 +714,6 @@ static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_MASK, status); } static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, @@ -760,8 +722,6 @@ static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_WRITE_INQUIRY_MODE, status); } static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, @@ -773,8 +733,6 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, if (!rp->status) hdev->inq_tx_power = rp->tx_power; - - hci_req_complete(hdev, HCI_OP_READ_INQ_RSP_TX_POWER, rp->status); } static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) @@ -782,8 +740,6 @@ static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_SET_EVENT_FLT, status); } static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -845,8 +801,6 @@ static void hci_cc_le_read_buffer_size(struct hci_dev *hdev, hdev->le_cnt = hdev->le_pkts; BT_DBG("%s le mtu %d:%d", hdev->name, hdev->le_mtu, hdev->le_pkts); - - hci_req_complete(hdev, HCI_OP_LE_READ_BUFFER_SIZE, rp->status); } static void hci_cc_le_read_local_features(struct hci_dev *hdev, @@ -858,8 +812,6 @@ static void hci_cc_le_read_local_features(struct hci_dev *hdev, if (!rp->status) memcpy(hdev->le_features, rp->features, 8); - - hci_req_complete(hdev, HCI_OP_LE_READ_LOCAL_FEATURES, rp->status); } static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, @@ -874,8 +826,6 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, if (!test_bit(HCI_INIT, &hdev->flags)) hci_update_ad(hdev); } - - hci_req_complete(hdev, HCI_OP_LE_READ_ADV_TX_POWER, rp->status); } static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) @@ -883,8 +833,6 @@ static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) __u8 status = *((__u8 *) skb->data); BT_DBG("%s status 0x%2.2x", hdev->name, status); - - hci_req_complete(hdev, HCI_OP_LE_SET_EVENT_MASK, status); } static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -985,8 +933,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (!test_bit(HCI_INIT, &hdev->flags)) hci_update_ad(hdev); - - hci_req_complete(hdev, HCI_OP_LE_SET_ADV_ENABLE, status); } static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) @@ -995,8 +941,6 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_PARAM, status); - if (status) { hci_dev_lock(hdev); mgmt_start_discovery_failed(hdev, status); @@ -1019,8 +963,6 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, switch (cp->enable) { case LE_SCANNING_ENABLED: - hci_req_complete(hdev, HCI_OP_LE_SET_SCAN_ENABLE, status); - if (status) { hci_dev_lock(hdev); mgmt_start_discovery_failed(hdev, status); @@ -1071,8 +1013,6 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, if (!rp->status) hdev->le_white_list_size = rp->size; - - hci_req_complete(hdev, HCI_OP_LE_READ_WHITE_LIST_SIZE, rp->status); } static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1083,8 +1023,6 @@ static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_REPLY, rp->status); } static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1095,8 +1033,6 @@ static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) if (rp->status) return; - - hci_req_complete(hdev, HCI_OP_LE_LTK_NEG_REPLY, rp->status); } static void hci_cc_le_read_supported_states(struct hci_dev *hdev, @@ -1108,8 +1044,6 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev, if (!rp->status) memcpy(hdev->le_states, rp->le_states, 8); - - hci_req_complete(hdev, HCI_OP_LE_READ_SUPPORTED_STATES, rp->status); } static void hci_cc_write_le_host_supported(struct hci_dev *hdev, @@ -1139,8 +1073,6 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, if (test_bit(HCI_MGMT, &hdev->dev_flags) && !test_bit(HCI_INIT, &hdev->flags)) mgmt_le_enable_complete(hdev, sent->le, status); - - hci_req_complete(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, status); } static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev, @@ -1162,7 +1094,6 @@ static void hci_cs_inquiry(struct hci_dev *hdev, __u8 status) BT_DBG("%s status 0x%2.2x", hdev->name, 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->dev_flags)) @@ -1694,7 +1625,6 @@ static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); hci_req_cmd_complete(hdev, HCI_OP_INQUIRY, status); - hci_req_complete(hdev, HCI_OP_INQUIRY, status); hci_conn_check_pending(hdev); -- cgit v1.2.3 From cecbb967b2f5c52e090978ff6afe7deddbfbeda5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:50 +0200 Subject: Bluetooth: Remove unused hdev->init_last_cmd This variable is no longer needed (due to async HCI request support and the conversion of hci_req_sync to use it), so it can be safely removed. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 6 ------ 1 file changed, 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6218eced153..3fc699db8fb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1015,10 +1015,7 @@ int hci_dev_open(__u16 dev) if (!test_bit(HCI_RAW, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - hdev->init_last_cmd = 0; - ret = __hci_init(hdev); - clear_bit(HCI_INIT, &hdev->flags); } @@ -2509,9 +2506,6 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) return -ENOMEM; } - if (test_bit(HCI_INIT, &hdev->flags)) - hdev->init_last_cmd = opcode; - /* Stand-alone HCI commands must be flaged as * single-command requests. */ -- cgit v1.2.3 From d865b0070485dfbb0611c5dc07fff21c440858a5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 5 Mar 2013 20:37:51 +0200 Subject: Bluetooth: Remove empty HCI event handlers With the removal of hci_req_complete() several HCI event handlers have essentially become empty and can be removed. The only potential benefit of these could have been logging, but the hci_event, hci_cmd_complete and hci_cmd_status already provide a log for events which they do not have an explicit handler for. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 164 ---------------------------------------------- 1 file changed, 164 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0dd85a0c05f..e89707f3394 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -424,13 +424,6 @@ static void hci_cc_write_voice_setting(struct hci_dev *hdev, hdev->notify(hdev, HCI_NOTIFY_VOICE_SETTING); } -static void hci_cc_host_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_cc_write_ssp_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -632,13 +625,6 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev, hdev->block_cnt, hdev->block_len); } -static void hci_cc_write_ca_timeout(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_cc_read_local_amp_info(struct hci_dev *hdev, struct sk_buff *skb) { @@ -701,29 +687,6 @@ a2mp_rsp: a2mp_send_create_phy_link_req(hdev, rp->status); } -static void hci_cc_delete_stored_link_key(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - -static void hci_cc_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - -static void hci_cc_write_inquiry_mode(struct hci_dev *hdev, - struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, struct sk_buff *skb) { @@ -735,13 +698,6 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, hdev->inq_tx_power = rp->tx_power; } -static void hci_cc_set_event_flt(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_pin_code_reply *rp = (void *) skb->data; @@ -828,13 +784,6 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, } } -static void hci_cc_le_set_event_mask(struct hci_dev *hdev, struct sk_buff *skb) -{ - __u8 status = *((__u8 *) skb->data); - - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_rp_user_confirm_reply *rp = (void *) skb->data; @@ -1015,26 +964,6 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, hdev->le_white_list_size = rp->size; } -static void hci_cc_le_ltk_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_le_ltk_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - - if (rp->status) - return; -} - -static void hci_cc_le_ltk_neg_reply(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_rp_le_ltk_neg_reply *rp = (void *) skb->data; - - BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - - if (rp->status) - return; -} - static void hci_cc_le_read_supported_states(struct hci_dev *hdev, struct sk_buff *skb) { @@ -1565,11 +1494,6 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, __u8 status) } } -static void hci_cs_le_start_enc(struct hci_dev *hdev, u8 status) -{ - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status) { struct hci_cp_create_phy_link *cp; @@ -1611,11 +1535,6 @@ static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status) amp_write_remote_assoc(hdev, cp->phy_handle); } -static void hci_cs_create_logical_link(struct hci_dev *hdev, u8 status) -{ - BT_DBG("%s status 0x%2.2x", hdev->name, status); -} - static void hci_inquiry_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); @@ -2172,17 +2091,6 @@ unlock: hci_dev_unlock(hdev); } -static void hci_remote_version_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static void hci_qos_setup_complete_evt(struct hci_dev *hdev, - struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_cmd_complete *ev = (void *) skb->data; @@ -2270,10 +2178,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_write_voice_setting(hdev, skb); break; - case HCI_OP_HOST_BUFFER_SIZE: - hci_cc_host_buffer_size(hdev, skb); - break; - case HCI_OP_WRITE_SSP_MODE: hci_cc_write_ssp_mode(hdev, skb); break; @@ -2306,10 +2210,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_data_block_size(hdev, skb); break; - case HCI_OP_WRITE_CA_TIMEOUT: - hci_cc_write_ca_timeout(hdev, skb); - break; - case HCI_OP_READ_FLOW_CONTROL_MODE: hci_cc_read_flow_control_mode(hdev, skb); break; @@ -2322,26 +2222,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_local_amp_assoc(hdev, skb); break; - case HCI_OP_DELETE_STORED_LINK_KEY: - hci_cc_delete_stored_link_key(hdev, skb); - break; - - case HCI_OP_SET_EVENT_MASK: - hci_cc_set_event_mask(hdev, skb); - break; - - case HCI_OP_WRITE_INQUIRY_MODE: - hci_cc_write_inquiry_mode(hdev, skb); - break; - case HCI_OP_READ_INQ_RSP_TX_POWER: hci_cc_read_inq_rsp_tx_power(hdev, skb); break; - case HCI_OP_SET_EVENT_FLT: - hci_cc_set_event_flt(hdev, skb); - break; - case HCI_OP_PIN_CODE_REPLY: hci_cc_pin_code_reply(hdev, skb); break; @@ -2366,10 +2250,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_adv_tx_power(hdev, skb); break; - case HCI_OP_LE_SET_EVENT_MASK: - hci_cc_le_set_event_mask(hdev, skb); - break; - case HCI_OP_USER_CONFIRM_REPLY: hci_cc_user_confirm_reply(hdev, skb); break; @@ -2402,14 +2282,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_le_read_white_list_size(hdev, skb); break; - case HCI_OP_LE_LTK_REPLY: - hci_cc_le_ltk_reply(hdev, skb); - break; - - case HCI_OP_LE_LTK_NEG_REPLY: - hci_cc_le_ltk_neg_reply(hdev, skb); - break; - case HCI_OP_LE_READ_SUPPORTED_STATES: hci_cc_le_read_supported_states(hdev, skb); break; @@ -2501,10 +2373,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_le_create_conn(hdev, ev->status); break; - case HCI_OP_LE_START_ENC: - hci_cs_le_start_enc(hdev, ev->status); - break; - case HCI_OP_CREATE_PHY_LINK: hci_cs_create_phylink(hdev, ev->status); break; @@ -2513,10 +2381,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cs_accept_phylink(hdev, ev->status); break; - case HCI_OP_CREATE_LOGICAL_LINK: - hci_cs_create_logical_link(hdev, ev->status); - break; - default: BT_DBG("%s opcode 0x%4.4x", hdev->name, opcode); break; @@ -3077,18 +2941,6 @@ unlock: hci_dev_unlock(hdev); } -static void hci_sync_conn_changed_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - BT_DBG("%s", hdev->name); -} - -static void hci_sniff_subrate_evt(struct hci_dev *hdev, struct sk_buff *skb) -{ - struct hci_ev_sniff_subrate *ev = (void *) skb->data; - - BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); -} - static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) { @@ -3816,14 +3668,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_remote_features_evt(hdev, skb); break; - case HCI_EV_REMOTE_VERSION: - hci_remote_version_evt(hdev, skb); - break; - - case HCI_EV_QOS_SETUP_COMPLETE: - hci_qos_setup_complete_evt(hdev, skb); - break; - case HCI_EV_CMD_COMPLETE: hci_cmd_complete_evt(hdev, skb); break; @@ -3880,14 +3724,6 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) hci_sync_conn_complete_evt(hdev, skb); break; - case HCI_EV_SYNC_CONN_CHANGED: - hci_sync_conn_changed_evt(hdev, skb); - break; - - case HCI_EV_SNIFF_SUBRATE: - hci_sniff_subrate_evt(hdev, skb); - break; - case HCI_EV_EXTENDED_INQUIRY_RESULT: hci_extended_inquiry_result_evt(hdev, skb); break; -- cgit v1.2.3 From bc4445c72cb5d1ed5af80c73e60214ab5ebd8e55 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:13 -0300 Subject: Bluetooth: Fix __hci_req_sync If hci_req_run returns error, we erroneously leave the current process in TASK_INTERRUPTABLE state. If we leave the process in TASK_INTERRUPTABLE and it is preempted, this process will never be scheduled again. This patch fixes this issue by moving the preparation for scheduling (add to waitqueue and set process state) to just after the hci_req_run call. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3fc699db8fb..5c643984728 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -95,15 +95,11 @@ static int __hci_req_sync(struct hci_dev *hdev, hdev->req_status = HCI_REQ_PEND; - add_wait_queue(&hdev->req_wait_q, &wait); - set_current_state(TASK_INTERRUPTIBLE); - func(&req, opt); err = hci_req_run(&req, hci_req_sync_complete); if (err < 0) { hdev->req_status = 0; - remove_wait_queue(&hdev->req_wait_q, &wait); /* req_run will fail if the request did not add any * commands to the queue, something that can happen when * a request with conditionals doesn't trigger any @@ -113,6 +109,9 @@ static int __hci_req_sync(struct hci_dev *hdev, return 0; } + add_wait_queue(&hdev->req_wait_q, &wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(timeout); remove_wait_queue(&hdev->req_wait_q, &wait); -- cgit v1.2.3 From 382b0c39b3f5b239e3fc07712027fcd4d503bd26 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:14 -0300 Subject: Bluetooth: Return ENODATA in hci_req_run In case the HCI request queue is empty, hci_req_run should return ENODATA instead of EINVAL. This way, hci_req_run returns a more meaningful error value. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5c643984728..d841046daed 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2452,7 +2452,7 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) /* Do not allow empty requests */ if (skb_queue_empty(&req->cmd_q)) - return -EINVAL; + return -ENODATA; skb = skb_peek_tail(&req->cmd_q); bt_cb(skb)->req.complete = complete; -- cgit v1.2.3 From 920c8300c66566afbf92311152c6e462a310203e Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:15 -0300 Subject: Bluetooth: Check hci_req_run returning value in __hci_req_sync Since hci_req_run will be returning more than one error code, we should check its returning value in __hci_req_sync. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d841046daed..4603464b91e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -100,13 +100,16 @@ static int __hci_req_sync(struct hci_dev *hdev, err = hci_req_run(&req, hci_req_sync_complete); if (err < 0) { hdev->req_status = 0; - /* req_run will fail if the request did not add any - * commands to the queue, something that can happen when - * a request with conditionals doesn't trigger any - * commands to be sent. This is normal behavior and - * should not trigger an error return. + + /* ENODATA means the HCI request command queue is empty. + * This can happen when a request with conditionals doesn't + * trigger any commands to be sent. This is normal behavior + * and should not trigger an error return. */ - return 0; + if (err == -ENODATA) + return 0; + + return err; } add_wait_queue(&hdev->req_wait_q, &wait); -- cgit v1.2.3 From 5d73e0342fd9bf500583868906325d42c4d2bf6f Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:16 -0300 Subject: Bluetooth: HCI request error handling When we are building a HCI request with more than one HCI command and one of the hci_req_add calls fail, we should have some cleanup routine so the HCI commands already queued on HCI request can be deleted. Otherwise, we will face some memory leaks issues. This patch implements the HCI request error handling which is the following: If a hci_req_add fails, we save the error code in hci_ request. Once hci_req_run is called, we verify the error field. If it is different from zero, we delete all HCI commands already queued and return the error code. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4603464b91e..b432baafdf1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2443,6 +2443,7 @@ void hci_req_init(struct hci_request *req, struct hci_dev *hdev) { skb_queue_head_init(&req->cmd_q); req->hdev = hdev; + req->err = 0; } int hci_req_run(struct hci_request *req, hci_req_complete_t complete) @@ -2453,6 +2454,14 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) BT_DBG("length %u", skb_queue_len(&req->cmd_q)); + /* If an error occured during request building, remove all HCI + * commands queued on the HCI request queue. + */ + if (req->err) { + skb_queue_purge(&req->cmd_q); + return req->err; + } + /* Do not allow empty requests */ if (skb_queue_empty(&req->cmd_q)) return -ENODATA; @@ -2529,7 +2538,9 @@ int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) skb = hci_prepare_cmd(hdev, opcode, plen, param); if (!skb) { - BT_ERR("%s no memory for command", hdev->name); + BT_ERR("%s no memory for command (opcode 0x%4.4x)", + hdev->name, opcode); + req->err = -ENOMEM; return -ENOMEM; } -- cgit v1.2.3 From e348fe6bbab85c513816d2536ffabac4be016442 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:17 -0300 Subject: Bluetooth: Make hci_req_add returning void Since no one checks the returning value of hci_req_add and HCI request errors are now handled in hci_req_run, we can make hci_ req_add returning void. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b432baafdf1..1c678757c83 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2529,7 +2529,7 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen, void *param) } /* Queue a command to an asynchronous HCI request */ -int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) +void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) { struct hci_dev *hdev = req->hdev; struct sk_buff *skb; @@ -2541,15 +2541,13 @@ int hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) BT_ERR("%s no memory for command (opcode 0x%4.4x)", hdev->name, opcode); req->err = -ENOMEM; - return -ENOMEM; + return; } if (skb_queue_empty(&req->cmd_q)) bt_cb(skb)->req.start = true; skb_queue_tail(&req->cmd_q, skb); - - return 0; } /* Get data from the previously sent command */ -- cgit v1.2.3 From 34739c1effcbdc6d210324e86514fa2d2d47b12b Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Fri, 8 Mar 2013 11:20:18 -0300 Subject: Bluetooth: Check req->err in hci_req_add If req->err is set, there is no point in queueing the HCI command in HCI request command queue since it won't be sent anyway. Signed-off-by: Andre Guedes Acked-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1c678757c83..02070dcdfbb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2536,6 +2536,12 @@ void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void *param) BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen); + /* If an error occured during request building, there is no point in + * queueing the HCI command. We can simply return. + */ + if (req->err) + return; + skb = hci_prepare_cmd(hdev, opcode, plen, param); if (!skb) { BT_ERR("%s no memory for command (opcode 0x%4.4x)", -- cgit v1.2.3 From ad82cdd196cc3e31c412a091e8dd59bef0331eaa Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 9 Mar 2013 09:53:50 +0200 Subject: Bluetooth: Fix endianness handling of cmd_status/complete opcodes The opcode in cmd_complete and cmd_status events is 16 bits, so we should only be comparing it after having converted it to the host endianness. There's already an opcode variable in both functions which is in host endiannes so the right fix is to just start using it instead of ev->opcode. Signed-off-by: Johan Hedberg Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e89707f3394..d11b87bc1d1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2299,10 +2299,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) break; } - if (ev->opcode != HCI_OP_NOP) + if (opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); - hci_req_cmd_complete(hdev, ev->opcode, status); + hci_req_cmd_complete(hdev, opcode, status); if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); @@ -2386,10 +2386,10 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) break; } - if (ev->opcode != HCI_OP_NOP) + if (opcode != HCI_OP_NOP) del_timer(&hdev->cmd_timer); - hci_req_cmd_status(hdev, ev->opcode, ev->status); + hci_req_cmd_status(hdev, opcode, ev->status); if (ev->ncmd && !test_bit(HCI_RESET, &hdev->flags)) { atomic_set(&hdev->cmd_cnt, 1); -- cgit v1.2.3 From 12033caf2380dbd28a497519eece9e92ccdca1c7 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghiu Date: Sat, 16 Mar 2013 16:10:03 +0200 Subject: Bluetooth: Use PTR_RET function Used PTR_RET function instead of IS_ERR and PTR_ERR. Patch found using coccinelle. Signed-off-by: Alexandru Gheorghiu Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_sysfs.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index 23b4e242a31..ff38561385d 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -590,10 +590,8 @@ int __init bt_sysfs_init(void) bt_debugfs = debugfs_create_dir("bluetooth", NULL); bt_class = class_create(THIS_MODULE, "bluetooth"); - if (IS_ERR(bt_class)) - return PTR_ERR(bt_class); - return 0; + return PTR_RET(bt_class); } void bt_sysfs_cleanup(void) -- cgit v1.2.3 From 5ae327f0efc12d35ea8c98007310c35c143c1e21 Mon Sep 17 00:00:00 2001 From: Alexandru Gheorghiu Date: Sun, 17 Mar 2013 07:16:50 +0200 Subject: Bluetooth: Replaced kzalloc and memcpy with kmemdup Replaced calls to kzalloc followed by memcpy with a single call to kmemdup. Signed-off-by: Alexandru Gheorghiu Signed-off-by: Gustavo Padovan --- net/bluetooth/a2mp.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index eb0f4b16ff0..17f33a62f6d 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -397,13 +397,12 @@ static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, if (ctrl) { u8 *assoc; - assoc = kzalloc(assoc_len, GFP_KERNEL); + assoc = kmemdup(rsp->amp_assoc, assoc_len, GFP_KERNEL); if (!assoc) { amp_ctrl_put(ctrl); return -ENOMEM; } - memcpy(assoc, rsp->amp_assoc, assoc_len); ctrl->assoc = assoc; ctrl->assoc_len = assoc_len; ctrl->assoc_rem_len = assoc_len; @@ -472,13 +471,12 @@ static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); u8 *assoc; - assoc = kzalloc(assoc_len, GFP_KERNEL); + assoc = kmemdup(req->amp_assoc, assoc_len, GFP_KERNEL); if (!assoc) { amp_ctrl_put(ctrl); return -ENOMEM; } - memcpy(assoc, req->amp_assoc, assoc_len); ctrl->assoc = assoc; ctrl->assoc_len = assoc_len; ctrl->assoc_rem_len = assoc_len; -- cgit v1.2.3 From 70da624376b8ba8d0db83eb817a7bc140778a26f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:51 -0500 Subject: Bluetooth: Move power on HCI command updates to their own function These commands will in a subsequent patch be performed in their own asynchronous request, so it's more readable (not just from a resulting code perspective but also the way the patches look like) to have them performed in their own function. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 78 ++++++++++++++++++++++++++++------------------------ 1 file changed, 42 insertions(+), 36 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 39395c7144a..7d58b44540a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3058,53 +3058,59 @@ static int set_bredr_scan(struct hci_dev *hdev) return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } -int mgmt_powered(struct hci_dev *hdev, u8 powered) +static int powered_update_hci(struct hci_dev *hdev) { - struct cmd_lookup match = { NULL, hdev }; - int err; + u8 link_sec; - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - return 0; + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + !lmp_host_ssp_capable(hdev)) { + u8 ssp = 1; - mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); + hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp); + } - if (powered) { - u8 link_sec; + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { + struct hci_cp_write_le_host_supported cp; - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && - !lmp_host_ssp_capable(hdev)) { - u8 ssp = 1; + cp.le = 1; + cp.simul = lmp_le_br_capable(hdev); - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp); - } + /* Check first if we already have the right + * host state (host features set) + */ + if (cp.le != lmp_host_le_capable(hdev) || + cp.simul != lmp_host_le_br_capable(hdev)) + hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, + sizeof(cp), &cp); + } - if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { - struct hci_cp_write_le_host_supported cp; + link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); + if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) + hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, + sizeof(link_sec), &link_sec); - cp.le = 1; - cp.simul = lmp_le_br_capable(hdev); + if (lmp_bredr_capable(hdev)) { + set_bredr_scan(hdev); + update_class(hdev); + update_name(hdev, hdev->dev_name); + update_eir(hdev); + } - /* Check first if we already have the right - * host state (host features set) - */ - if (cp.le != lmp_host_le_capable(hdev) || - cp.simul != lmp_host_le_br_capable(hdev)) - hci_send_cmd(hdev, - HCI_OP_WRITE_LE_HOST_SUPPORTED, - sizeof(cp), &cp); - } + return 0; +} - link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); - if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, - sizeof(link_sec), &link_sec); +int mgmt_powered(struct hci_dev *hdev, u8 powered) +{ + struct cmd_lookup match = { NULL, hdev }; + int err; - if (lmp_bredr_capable(hdev)) { - set_bredr_scan(hdev); - update_class(hdev); - update_name(hdev, hdev->dev_name); - update_eir(hdev); - } + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + return 0; + + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); + + if (powered) { + powered_update_hci(hdev); } else { u8 status = MGMT_STATUS_NOT_POWERED; u8 zero_cod[] = { 0, 0, 0 }; -- cgit v1.2.3 From 890ea8988f7d17453515122041adb0e1acdb6025 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:52 -0500 Subject: Bluetooth: Update mgmt powered HCI commands to use async requests This patch updates sending of HCI commands related to mgmt_set_powered (e.g. class, name and EIR data) to be sent using asynchronous requests. This is necessary since it's the only (well, at least the cleanest) way to keep the power on procedure synchronized and let user space know it has completed only when all HCI commands are completed (this actual fix is coming in a subsequent patch). Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 163 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 100 insertions(+), 63 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7d58b44540a..4726876298f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -591,32 +591,33 @@ static void create_eir(struct hci_dev *hdev, u8 *data) ptr = create_uuid128_list(hdev, ptr, HCI_MAX_EIR_LENGTH - (ptr - data)); } -static int update_eir(struct hci_dev *hdev) +static void update_eir(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_eir cp; if (!hdev_is_powered(hdev)) - return 0; + return; if (!lmp_ext_inq_capable(hdev)) - return 0; + return; if (!test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - return 0; + return; if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return 0; + return; memset(&cp, 0, sizeof(cp)); create_eir(hdev, cp.data); if (memcmp(cp.data, hdev->eir, sizeof(cp.data)) == 0) - return 0; + return; memcpy(hdev->eir, cp.data, sizeof(cp.data)); - return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } static u8 get_service_classes(struct hci_dev *hdev) @@ -630,47 +631,50 @@ static u8 get_service_classes(struct hci_dev *hdev) return val; } -static int update_class(struct hci_dev *hdev) +static void update_class(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; u8 cod[3]; - int err; BT_DBG("%s", hdev->name); if (!hdev_is_powered(hdev)) - return 0; + return; if (test_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) - return 0; + return; cod[0] = hdev->minor_class; cod[1] = hdev->major_class; cod[2] = get_service_classes(hdev); if (memcmp(cod, hdev->dev_class, 3) == 0) - return 0; + return; - err = hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - if (err == 0) - set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); + hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - return err; + set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); } static void service_cache_off(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, service_cache.work); + struct hci_request req; if (!test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) return; + hci_req_init(&req, hdev); + hci_dev_lock(hdev); - update_eir(hdev); - update_class(hdev); + update_eir(&req); + update_class(&req); hci_dev_unlock(hdev); + + hci_req_run(&req, NULL); } static void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) @@ -1355,6 +1359,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; struct pending_cmd *cmd; + struct hci_request req; struct bt_uuid *uuid; int err; @@ -1380,13 +1385,12 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) list_add_tail(&uuid->list, &hdev->uuids); - err = update_class(hdev); - if (err < 0) - goto failed; + hci_req_init(&req, hdev); - err = update_eir(hdev); - if (err < 0) - goto failed; + update_class(&req); + update_eir(&req); + + hci_req_run(&req, NULL); if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, @@ -1395,8 +1399,12 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) } cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); - if (!cmd) + if (!cmd) { err = -ENOMEM; + goto failed; + } + + err = 0; failed: hci_dev_unlock(hdev); @@ -1424,6 +1432,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, struct pending_cmd *cmd; struct bt_uuid *match, *tmp; u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + struct hci_request req; int err, found; BT_DBG("request for %s", hdev->name); @@ -1466,13 +1475,12 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, } update_class: - err = update_class(hdev); - if (err < 0) - goto unlock; + hci_req_init(&req, hdev); - err = update_eir(hdev); - if (err < 0) - goto unlock; + update_class(&req); + update_eir(&req); + + hci_req_run(&req, NULL); if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, @@ -1481,8 +1489,12 @@ update_class: } cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); - if (!cmd) + if (!cmd) { err = -ENOMEM; + goto unlock; + } + + err = 0; unlock: hci_dev_unlock(hdev); @@ -1494,6 +1506,7 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_cp_set_dev_class *cp = data; struct pending_cmd *cmd; + struct hci_request req; int err; BT_DBG("request for %s", hdev->name); @@ -1521,16 +1534,18 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, goto unlock; } + hci_req_init(&req, hdev); + if (test_and_clear_bit(HCI_SERVICE_CACHE, &hdev->dev_flags)) { hci_dev_unlock(hdev); cancel_delayed_work_sync(&hdev->service_cache); hci_dev_lock(hdev); - update_eir(hdev); + update_eir(&req); } - err = update_class(hdev); - if (err < 0) - goto unlock; + update_class(&req); + + hci_req_run(&req, NULL); if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, @@ -1539,8 +1554,12 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, } cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); - if (!cmd) + if (!cmd) { err = -ENOMEM; + goto unlock; + } + + err = 0; unlock: hci_dev_unlock(hdev); @@ -2268,13 +2287,13 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, HCI_OP_USER_PASSKEY_NEG_REPLY, 0); } -static int update_name(struct hci_dev *hdev, const char *name) +static void update_name(struct hci_request *req, const char *name) { struct hci_cp_write_local_name cp; memcpy(cp.name, name, sizeof(cp.name)); - return hci_send_cmd(hdev, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); } static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, @@ -2282,6 +2301,7 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_cp_set_local_name *cp = data; struct pending_cmd *cmd; + struct hci_request req; int err; BT_DBG(""); @@ -2310,7 +2330,9 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } - err = update_name(hdev, cp->name); + hci_req_init(&req, hdev); + update_name(&req, cp->name); + err = hci_req_run(&req, NULL); if (err < 0) mgmt_pending_remove(cmd); @@ -2698,6 +2720,7 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_set_device_id *cp = data; + struct hci_request req; int err; __u16 source; @@ -2718,7 +2741,9 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, NULL, 0); - update_eir(hdev); + hci_req_init(&req, hdev); + update_eir(&req); + hci_req_run(&req, NULL); hci_dev_unlock(hdev); @@ -3043,8 +3068,9 @@ static void settings_rsp(struct pending_cmd *cmd, void *data) mgmt_pending_free(cmd); } -static int set_bredr_scan(struct hci_dev *hdev) +static void set_bredr_scan(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; u8 scan = 0; if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) @@ -3052,21 +3078,22 @@ static int set_bredr_scan(struct hci_dev *hdev) if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) scan |= SCAN_INQUIRY; - if (!scan) - return 0; - - return hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + if (scan) + hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } static int powered_update_hci(struct hci_dev *hdev) { + struct hci_request req; u8 link_sec; + hci_req_init(&req, hdev); + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && !lmp_host_ssp_capable(hdev)) { u8 ssp = 1; - hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &ssp); + hci_req_add(&req, HCI_OP_WRITE_SSP_MODE, 1, &ssp); } if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) { @@ -3080,23 +3107,23 @@ static int powered_update_hci(struct hci_dev *hdev) */ if (cp.le != lmp_host_le_capable(hdev) || cp.simul != lmp_host_le_br_capable(hdev)) - hci_send_cmd(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED, - sizeof(cp), &cp); + hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, + sizeof(cp), &cp); } link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags); if (link_sec != test_bit(HCI_AUTH, &hdev->flags)) - hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, - sizeof(link_sec), &link_sec); + hci_req_add(&req, HCI_OP_WRITE_AUTH_ENABLE, + sizeof(link_sec), &link_sec); if (lmp_bredr_capable(hdev)) { - set_bredr_scan(hdev); - update_class(hdev); - update_name(hdev, hdev->dev_name); - update_eir(hdev); + set_bredr_scan(&req); + update_class(&req); + update_name(&req, hdev->dev_name); + update_eir(&req); } - return 0; + return hci_req_run(&req, NULL); } int mgmt_powered(struct hci_dev *hdev, u8 powered) @@ -3561,23 +3588,25 @@ int mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) return err; } -static int clear_eir(struct hci_dev *hdev) +static void clear_eir(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_eir cp; if (!lmp_ext_inq_capable(hdev)) - return 0; + return; memset(hdev->eir, 0, sizeof(hdev->eir)); memset(&cp, 0, sizeof(cp)); - return hci_send_cmd(hdev, HCI_OP_WRITE_EIR, sizeof(cp), &cp); + hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp); } int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) { struct cmd_lookup match = { NULL, hdev }; + struct hci_request req; bool changed = false; int err = 0; @@ -3610,10 +3639,14 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) if (match.sk) sock_put(match.sk); + hci_req_init(&req, hdev); + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) - update_eir(hdev); + update_eir(&req); else - clear_eir(hdev); + clear_eir(&req); + + hci_req_run(&req, NULL); return err; } @@ -3701,8 +3734,12 @@ send_event: * adapter so only update them here if this is a name change * unrelated to power on. */ - if (!test_bit(HCI_INIT, &hdev->flags)) - update_eir(hdev); + if (!test_bit(HCI_INIT, &hdev->flags)) { + struct hci_request req; + hci_req_init(&req, hdev); + update_eir(&req); + hci_req_run(&req, NULL); + } failed: if (cmd) -- cgit v1.2.3 From 229ab39caf8c1321527e408725c1350f7c9aaa84 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:53 -0500 Subject: Bluetooth: Wait for HCI command completion with mgmt_set_powered We should only notify user space that the adapter has been powered on after all HCI commands related to the action have completed. This patch fixes the issue by instating an async request complete callback for these HCI commands and only notifies user space in the callback. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 46 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 12 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4726876298f..bf17a62a1be 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3082,6 +3082,24 @@ static void set_bredr_scan(struct hci_request *req) hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } +static void powered_complete(struct hci_dev *hdev, u8 status) +{ + struct cmd_lookup match = { NULL, hdev }; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); + + new_settings(hdev, match.sk); + + hci_dev_unlock(hdev); + + if (match.sk) + sock_put(match.sk); +} + static int powered_update_hci(struct hci_dev *hdev) { struct hci_request req; @@ -3123,32 +3141,36 @@ static int powered_update_hci(struct hci_dev *hdev) update_eir(&req); } - return hci_req_run(&req, NULL); + return hci_req_run(&req, powered_complete); } int mgmt_powered(struct hci_dev *hdev, u8 powered) { struct cmd_lookup match = { NULL, hdev }; + u8 status_not_powered = MGMT_STATUS_NOT_POWERED; + u8 zero_cod[] = { 0, 0, 0 }; int err; if (!test_bit(HCI_MGMT, &hdev->dev_flags)) return 0; - mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); - if (powered) { - powered_update_hci(hdev); - } else { - u8 status = MGMT_STATUS_NOT_POWERED; - u8 zero_cod[] = { 0, 0, 0 }; - - mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); + if (powered_update_hci(hdev) == 0) + return 0; - if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) - mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, - zero_cod, sizeof(zero_cod), NULL); + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, + &match); + goto new_settings; } + mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status_not_powered); + + if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) + mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, + zero_cod, sizeof(zero_cod), NULL); + +new_settings: err = new_settings(hdev, match.sk); if (match.sk) -- cgit v1.2.3 From 0cab9c80ffc5006bf0f6922d805a7540e4949877 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:54 -0500 Subject: Bluetooth: Fix busy condition testing for EIR and class updates The add/remove_uuid and set_dev_class mgmt commands can trigger both EIR and class HCI commands, so testing just for a pending class command is enough. The simplest way to monitor conflicts that should trigger "busy" error returns is to check for any pending mgmt command that can trigger these HCI commands. This patch adds a helper function for this (pending_eir_or_class) and uses it instead of the old HCI_PENDING_CLASS flag to test for busy conditions. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bf17a62a1be..367837d0da2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1336,6 +1336,29 @@ unlock: return err; } +/* This is a helper function to test for pending mgmt commands that can + * cause CoD or EIR HCI commands. We can only allow one such pending + * mgmt command at a time since otherwise we cannot easily track what + * the current values are, will be, and based on that calculate if a new + * HCI command needs to be sent and if yes with what value. + */ +static bool pending_eir_or_class(struct hci_dev *hdev) +{ + struct pending_cmd *cmd; + + list_for_each_entry(cmd, &hdev->mgmt_pending, list) { + switch (cmd->opcode) { + case MGMT_OP_ADD_UUID: + case MGMT_OP_REMOVE_UUID: + case MGMT_OP_SET_DEV_CLASS: + case MGMT_OP_SET_POWERED: + return true; + } + } + + return false; +} + static const u8 bluetooth_base_uuid[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -1367,7 +1390,7 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_dev_lock(hdev); - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { + if (pending_eir_or_class(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID, MGMT_STATUS_BUSY); goto failed; @@ -1439,7 +1462,7 @@ static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { + if (pending_eir_or_class(hdev)) { err = cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, MGMT_STATUS_BUSY); goto unlock; @@ -1515,15 +1538,19 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, MGMT_STATUS_NOT_SUPPORTED); - if (test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) - return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_BUSY); + hci_dev_lock(hdev); - if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) - return cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, - MGMT_STATUS_INVALID_PARAMS); + if (pending_eir_or_class(hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_BUSY); + goto unlock; + } - hci_dev_lock(hdev); + if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, + MGMT_STATUS_INVALID_PARAMS); + goto unlock; + } hdev->major_class = cp->major; hdev->minor_class = cp->minor; -- cgit v1.2.3 From 92da609750e75d5f46e809fd42e0cace61f6f4d5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:55 -0500 Subject: Bluetooth: Fix UUID/class mgmt command response synchronization We should only return a mgmt command complete once all HCI commands to a mgmt_set_dev_class or mgmt_add/remove_uuid command have completed. This patch fixes the issue by having a proper async request complete callback for these actions and responding to user space in the callback. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 73 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 56 insertions(+), 17 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 367837d0da2..8a0bbb914be 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1378,6 +1378,32 @@ static u8 get_uuid_size(const u8 *uuid) return 16; } +static void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status) +{ + struct pending_cmd *cmd; + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(mgmt_op, hdev); + if (!cmd) + goto unlock; + + cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status), + hdev->dev_class, 3); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + +static void add_uuid_complete(struct hci_dev *hdev, u8 status) +{ + BT_DBG("status 0x%02x", status); + + mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status); +} + static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_add_uuid *cp = data; @@ -1413,9 +1439,11 @@ static int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) update_class(&req); update_eir(&req); - hci_req_run(&req, NULL); + err = hci_req_run(&req, add_uuid_complete); + if (err < 0) { + if (err != -ENODATA) + goto failed; - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, hdev->dev_class, 3); goto failed; @@ -1448,6 +1476,13 @@ static bool enable_service_cache(struct hci_dev *hdev) return false; } +static void remove_uuid_complete(struct hci_dev *hdev, u8 status) +{ + BT_DBG("status 0x%02x", status); + + mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status); +} + static int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -1503,9 +1538,11 @@ update_class: update_class(&req); update_eir(&req); - hci_req_run(&req, NULL); + err = hci_req_run(&req, remove_uuid_complete); + if (err < 0) { + if (err != -ENODATA) + goto unlock; - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, hdev->dev_class, 3); goto unlock; @@ -1524,6 +1561,13 @@ unlock: return err; } +static void set_class_complete(struct hci_dev *hdev, u8 status) +{ + BT_DBG("status 0x%02x", status); + + mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status); +} + static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -1572,9 +1616,11 @@ static int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, update_class(&req); - hci_req_run(&req, NULL); + err = hci_req_run(&req, set_class_complete); + if (err < 0) { + if (err != -ENODATA) + goto unlock; - if (!test_bit(HCI_PENDING_CLASS, &hdev->dev_flags)) { err = cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, hdev->dev_class, 3); goto unlock; @@ -3700,21 +3746,14 @@ int mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) return err; } -static void class_rsp(struct pending_cmd *cmd, void *data) +static void sk_lookup(struct pending_cmd *cmd, void *data) { struct cmd_lookup *match = data; - cmd_complete(cmd->sk, cmd->index, cmd->opcode, match->mgmt_status, - match->hdev->dev_class, 3); - - list_del(&cmd->list); - if (match->sk == NULL) { match->sk = cmd->sk; sock_hold(match->sk); } - - mgmt_pending_free(cmd); } int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, @@ -3725,9 +3764,9 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, class_rsp, &match); - mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, class_rsp, &match); - mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, class_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match); + mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match); + mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); if (!status) err = mgmt_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, -- cgit v1.2.3 From 2908fe31cf6b8d3a975efb567347f85e724f4e81 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:56 -0500 Subject: Bluetooth: Remove useless HCI_PENDING_CLASS flag Now that class related operations are tracked through asynchronous HCI requests this flag is no longer needed. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 3 +-- net/bluetooth/mgmt.c | 4 ---- 2 files changed, 1 insertion(+), 6 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index d11b87bc1d1..5f2d008f335 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -194,8 +194,7 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); /* Reset all non-persistent flags */ - hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PENDING_CLASS) | - BIT(HCI_PERIODIC_INQ)); + hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ)); hdev->discovery.state = DISCOVERY_STOPPED; hdev->inq_tx_power = HCI_TX_POWER_INVALID; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8a0bbb914be..3e3cb0102b1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -652,8 +652,6 @@ static void update_class(struct hci_request *req) return; hci_req_add(req, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); - - set_bit(HCI_PENDING_CLASS, &hdev->dev_flags); } static void service_cache_off(struct work_struct *work) @@ -3762,8 +3760,6 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; int err = 0; - clear_bit(HCI_PENDING_CLASS, &hdev->dev_flags); - mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match); mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match); mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); -- cgit v1.2.3 From 2cc6fb0049bc02ca7a020ba7b4f88b4c35976058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:57 -0500 Subject: Bluetooth: Add a define for the HCI persistent flags mask We'll need to use this mask also when powering off the HCI device so it's better to have this in a single and visible place. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5f2d008f335..ed4ecd930a7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -194,7 +194,7 @@ static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_RESET, &hdev->flags); /* Reset all non-persistent flags */ - hdev->dev_flags &= ~(BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ)); + hdev->dev_flags &= ~HCI_PERSISTENT_MASK; hdev->discovery.state = DISCOVERY_STOPPED; hdev->inq_tx_power = HCI_TX_POWER_INVALID; -- cgit v1.2.3 From f9f85279fd3a3284023231c7f0796f98c417e7cd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:58 -0500 Subject: Bluetooth: Clear non-persistent flags when closing HCI device When hci_dev_do_close() is called we should make sure to clear all non-persistent flags in hci->dev_flags. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 02070dcdfbb..059bbae534d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1139,6 +1139,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Clear flags */ hdev->flags = 0; + hdev->dev_flags &= ~HCI_PERSISTENT_MASK; /* Controller radio is available but is currently powered down */ hdev->amp_status = 0; -- cgit v1.2.3 From 35b973c9dd6d518491b251ac777d767d7820aa37 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:06:59 -0500 Subject: Bluetooth: Fix clearing flags on power off before notifying mgmt When powering off the device the hdev->flags and hdev->dev_flags need to be cleared before calling mgmt_powered(). If this is not done the resulting events sent to user space may contain incorrect values. Note that the HCI_AUTO_OFF flag accessed right after this is part of the persistent flags, so it's unchanged by the hdev->dev_flags reset. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 059bbae534d..9e87a91562a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1130,6 +1130,10 @@ static int hci_dev_do_close(struct hci_dev *hdev) * and no tasks are scheduled. */ hdev->close(hdev); + /* Clear flags */ + hdev->flags = 0; + hdev->dev_flags &= ~HCI_PERSISTENT_MASK; + if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); @@ -1137,10 +1141,6 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_unlock(hdev); } - /* Clear flags */ - hdev->flags = 0; - hdev->dev_flags &= ~HCI_PERSISTENT_MASK; - /* Controller radio is available but is currently powered down */ hdev->amp_status = 0; -- cgit v1.2.3 From 13928971396fb5ad022ec65f694cea367ca48504 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:00 -0500 Subject: Bluetooth: Fix waiting for EIR update when setting local name We shouldn't respond to the mgmt_set_local_name command until all related HCI commands have completed. This patch fixes the issue by running the local name HCI command and the EIR update in the same asynchronous request, and returning the mgmt command complete through the complete callback of the request. The downside of this is that we must set hdev->dev_name before the local name HCI command has completed since otherwise the generated EIR command doesn't contain the new name. This means that we can no-longer reliably detect when the name has really changed and when not. Luckily this only affects scenarios where the mgmt interface is *not* used (e.g. hciconfig) so redundant mgmt_ev_local_name_changed events in these cases are an acceptable drawback. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 96 +++++++++++++++++++++++++++------------------------- 1 file changed, 49 insertions(+), 47 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 3e3cb0102b1..1d50841fa70 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2358,15 +2358,44 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, HCI_OP_USER_PASSKEY_NEG_REPLY, 0); } -static void update_name(struct hci_request *req, const char *name) +static void update_name(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_local_name cp; - memcpy(cp.name, name, sizeof(cp.name)); + memcpy(cp.name, hdev->dev_name, sizeof(cp.name)); hci_req_add(req, HCI_OP_WRITE_LOCAL_NAME, sizeof(cp), &cp); } +static void set_name_complete(struct hci_dev *hdev, u8 status) +{ + struct mgmt_cp_set_local_name *cp; + struct pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); + if (!cmd) + goto unlock; + + cp = cmd->param; + + if (status) + cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, + mgmt_status(status)); + else + cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, + cp, sizeof(*cp)); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -2401,9 +2430,12 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, goto failed; } + memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); + hci_req_init(&req, hdev); - update_name(&req, cp->name); - err = hci_req_run(&req, NULL); + update_name(&req); + update_eir(&req); + err = hci_req_run(&req, set_name_complete); if (err < 0) mgmt_pending_remove(cmd); @@ -3208,7 +3240,7 @@ static int powered_update_hci(struct hci_dev *hdev) if (lmp_bredr_capable(hdev)) { set_bredr_scan(&req); update_class(&req); - update_name(&req, hdev->dev_name); + update_name(&req); update_eir(&req); } @@ -3776,59 +3808,29 @@ int mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, int mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) { - struct pending_cmd *cmd; struct mgmt_cp_set_local_name ev; - bool changed = false; - int err = 0; + struct pending_cmd *cmd; - if (memcmp(name, hdev->dev_name, sizeof(hdev->dev_name)) != 0) { - memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); - changed = true; - } + if (status) + return 0; memset(&ev, 0, sizeof(ev)); memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); cmd = mgmt_pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); - if (!cmd) - goto send_event; - - /* Always assume that either the short or the complete name has - * changed if there was a pending mgmt command */ - changed = true; - - if (status) { - err = cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, - mgmt_status(status)); - goto failed; - } - - err = cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, &ev, - sizeof(ev)); - if (err < 0) - goto failed; - -send_event: - if (changed) - err = mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, - sizeof(ev), cmd ? cmd->sk : NULL); + if (!cmd) { + memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); - /* EIR is taken care of separately when powering on the - * adapter so only update them here if this is a name change - * unrelated to power on. - */ - if (!test_bit(HCI_INIT, &hdev->flags)) { - struct hci_request req; - hci_req_init(&req, hdev); - update_eir(&req); - hci_req_run(&req, NULL); + /* If this is a HCI command related to powering on the + * HCI dev don't send any mgmt signals. + */ + if (mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) + return 0; } -failed: - if (cmd) - mgmt_pending_remove(cmd); - return err; + return mgmt_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), + cmd ? cmd->sk : NULL); } int mgmt_read_local_oob_data_reply_complete(struct hci_dev *hdev, u8 *hash, -- cgit v1.2.3 From 04b4edcbc9049e100681c0149b572de439be42ab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:01 -0500 Subject: Bluetooth: Handle AD updating through an async request For proper control of the AD update and the related HCI commands it's best to run the AD update through an async request instead of a standalone HCI command. This patch changes the hci_update_ad() function to take a request pointer and updates its users appropriately. E.g. the function is no longer called after the init sequence but during stage 3 of the init sequence. The TX power is read during the init sequence, so we don't need an explicit update whenever it is read and the AD update based on the local name should be done through the local name mgmt handler. The only other user is the update based on enabling advertising. This part is still kept as there is no mgmt API to enable it. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 29 ++++++++++------------------- net/bluetooth/hci_event.c | 19 +++++++++---------- 2 files changed, 19 insertions(+), 29 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9e87a91562a..0ffd3587117 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -492,8 +492,10 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) if (hdev->commands[5] & 0x10) hci_setup_link_policy(req); - if (lmp_le_capable(hdev)) + if (lmp_le_capable(hdev)) { hci_set_le_support(req); + hci_update_ad(req); + } } static int __hci_init(struct hci_dev *hdev) @@ -936,39 +938,29 @@ static u8 create_ad(struct hci_dev *hdev, u8 *ptr) return ad_len; } -int hci_update_ad(struct hci_dev *hdev) +void hci_update_ad(struct hci_request *req) { + struct hci_dev *hdev = req->hdev; struct hci_cp_le_set_adv_data cp; u8 len; - int err; - - hci_dev_lock(hdev); - if (!lmp_le_capable(hdev)) { - err = -EINVAL; - goto unlock; - } + if (!lmp_le_capable(hdev)) + return; memset(&cp, 0, sizeof(cp)); len = create_ad(hdev, cp.data); if (hdev->adv_data_len == len && - memcmp(cp.data, hdev->adv_data, len) == 0) { - err = 0; - goto unlock; - } + memcmp(cp.data, hdev->adv_data, len) == 0) + return; memcpy(hdev->adv_data, cp.data, sizeof(cp.data)); hdev->adv_data_len = len; cp.length = len; - err = hci_send_cmd(hdev, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); -unlock: - hci_dev_unlock(hdev); - - return err; + hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); } /* ---- HCI ioctl helpers ---- */ @@ -1025,7 +1017,6 @@ int hci_dev_open(__u16 dev) hci_dev_hold(hdev); set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); - hci_update_ad(hdev); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && mgmt_valid_hdev(hdev)) { hci_dev_lock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ed4ecd930a7..84edacbc14a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -223,9 +223,6 @@ static void hci_cc_write_local_name(struct hci_dev *hdev, struct sk_buff *skb) memcpy(hdev->dev_name, sent, HCI_MAX_NAME_LENGTH); hci_dev_unlock(hdev); - - if (!status && !test_bit(HCI_INIT, &hdev->flags)) - hci_update_ad(hdev); } static void hci_cc_read_local_name(struct hci_dev *hdev, struct sk_buff *skb) @@ -776,11 +773,8 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) { + if (!rp->status) hdev->adv_tx_power = rp->tx_power; - if (!test_bit(HCI_INIT, &hdev->flags)) - hci_update_ad(hdev); - } } static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -877,10 +871,15 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_LE_PERIPHERAL, &hdev->dev_flags); } - hci_dev_unlock(hdev); + if (!test_bit(HCI_INIT, &hdev->flags)) { + struct hci_request req; - if (!test_bit(HCI_INIT, &hdev->flags)) - hci_update_ad(hdev); + hci_req_init(&req, hdev); + hci_update_ad(&req); + hci_req_run(&req, NULL); + } + + hci_dev_unlock(hdev); } static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) -- cgit v1.2.3 From 3f985050fa5e2f6715b215452198f005df2d5746 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:02 -0500 Subject: Bluetooth: Fix local name setting for LE-only controllers This patch fixes the mgmt_set_local_name command to send the appropriate HCI commands based on BR/EDR support and LE support. Local name and EIR data should only be sent for BR/EDR capable controllers whereas an update to the AD should only happen for LE capable controllers. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1d50841fa70..28e5975246c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2433,8 +2433,15 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); hci_req_init(&req, hdev); - update_name(&req); - update_eir(&req); + + if (lmp_bredr_capable(hdev)) { + update_name(&req); + update_eir(&req); + } + + if (lmp_le_capable(hdev)) + hci_update_ad(&req); + err = hci_req_run(&req, set_name_complete); if (err < 0) mgmt_pending_remove(cmd); -- cgit v1.2.3 From b3f2ca9446f63acf5ab8552a37c4cc90af64b816 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:03 -0500 Subject: Bluetooth: Fix setting local name to the existing value If user space attempts to set the local name to the same value that's already set we should simply return a direct command complete for this mgmt command. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 28e5975246c..15305fa5506 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2408,6 +2408,17 @@ static int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); + /* If the old values are the same as the new ones just return a + * direct command complete event. + */ + if (!memcmp(hdev->dev_name, cp->name, sizeof(hdev->dev_name)) && + !memcmp(hdev->short_name, cp->short_name, + sizeof(hdev->short_name))) { + err = cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, + data, len); + goto failed; + } + memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); if (!hdev_is_powered(hdev)) { -- cgit v1.2.3 From 2b76f4539c6a41c3dd2e73f9ca7e03bcff6c8774 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:04 -0500 Subject: Bluetooth: Use an async request for mgmt_set_connectable This patch changes the mgmt_set_connectable handler to use an async request for sending the required HCI command. This is necessary preparation for handling the fast connectable change that needs to be associated with disabling the connectable setting. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 15305fa5506..a4f928ddc28 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -996,11 +996,32 @@ failed: return err; } +static void set_connectable_complete(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev); + if (!cmd) + goto unlock; + + send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct pending_cmd *cmd; + struct hci_request req; u8 scan; int err; @@ -1067,7 +1088,11 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, cancel_delayed_work(&hdev->discov_off); } - err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + + err = hci_req_run(&req, set_connectable_complete); if (err < 0) mgmt_pending_remove(cmd); @@ -3328,7 +3353,7 @@ int mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) int mgmt_connectable(struct hci_dev *hdev, u8 connectable) { - struct cmd_lookup match = { NULL, hdev }; + struct pending_cmd *cmd; bool changed = false; int err = 0; @@ -3340,14 +3365,10 @@ int mgmt_connectable(struct hci_dev *hdev, u8 connectable) changed = true; } - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, settings_rsp, - &match); + cmd = mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev); if (changed) - err = new_settings(hdev, match.sk); - - if (match.sk) - sock_put(match.sk); + err = new_settings(hdev, cmd ? cmd->sk : NULL); return err; } -- cgit v1.2.3 From 33e38b3e13e313baedd7c56c38ad249f230171d2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:05 -0500 Subject: Bluetooth: Fix fast connectable response sending The mgmt_set_fast_connectable response should be sent only when all related HCI commands have completed. This patch fixes the issue by using an async request and sending the response to user space throught the complete callback of the request. The patch also fixes in the same go the return parameters of the command which should be the current settings. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 53 +++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 11 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a4f928ddc28..bd61318b647 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2896,11 +2896,39 @@ static int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, return err; } +static void fast_connectable_complete(struct hci_dev *hdev, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status 0x%02x", status); + + hci_dev_lock(hdev); + + cmd = mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev); + if (!cmd) + goto unlock; + + if (status) { + cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + mgmt_status(status)); + } else { + send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); + new_settings(hdev, cmd->sk); + } + + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); +} + static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; struct hci_cp_write_page_scan_activity acp; + struct pending_cmd *cmd; + struct hci_request req; u8 type; int err; @@ -2939,25 +2967,28 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, /* default 11.25 msec page scan window */ acp.window = __constant_cpu_to_le16(0x0012); - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), - &acp); - if (err < 0) { - err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, - MGMT_STATUS_FAILED); - goto done; + cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev, + data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; } - err = hci_send_cmd(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + hci_req_init(&req, hdev); + + hci_req_add(&req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), &acp); + hci_req_add(&req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + + err = hci_req_run(&req, fast_connectable_complete); if (err < 0) { err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_FAILED); - goto done; + mgmt_pending_remove(cmd); } - err = cmd_complete(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 0, - NULL, 0); -done: +unlock: hci_dev_unlock(hdev); + return err; } -- cgit v1.2.3 From 1a47aee85f8a0803b879abb2e331d6354eb975ac Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:06 -0500 Subject: Bluetooth: Limit fast connectable support to >= 1.2 controllers The HCI commands that are necessary for fast connectable mode are only available from HCI specification version 1.2 onwards. This should be reflected in the supported settings as well as error response for the set_fast_connectable command when dealing with a < 1.2 capable controller. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bd61318b647..34caf30584c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -384,7 +384,8 @@ static u32 get_supported_settings(struct hci_dev *hdev) if (lmp_bredr_capable(hdev)) { settings |= MGMT_SETTING_CONNECTABLE; - settings |= MGMT_SETTING_FAST_CONNECTABLE; + if (hdev->hci_ver >= BLUETOOTH_VER_1_2) + settings |= MGMT_SETTING_FAST_CONNECTABLE; settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; @@ -2934,7 +2935,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); - if (!lmp_bredr_capable(hdev)) + if (!lmp_bredr_capable(hdev) || hdev->hci_ver < BLUETOOTH_VER_1_2) return cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, MGMT_STATUS_NOT_SUPPORTED); -- cgit v1.2.3 From 05cbf29f84f2cf17554b58a3ab4a0ac46d52eca6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:07 -0500 Subject: Bluetooth: Fix error response for simultaneous fast connectable commands If there's another pending mgmt_set_fast_connectable command we should return a "busy" error response. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 34caf30584c..e89938e0233 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2953,6 +2953,12 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); + if (mgmt_pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { + err = cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, + MGMT_STATUS_BUSY); + goto unlock; + } + if (cp->val) { type = PAGE_SCAN_TYPE_INTERLACED; -- cgit v1.2.3 From 1a4d3c4b3750885733641216756de4e4d9b2443a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:08 -0500 Subject: Bluetooth: Add proper flag for fast connectable mode In order to be able to represent fast connectable mode in the mgmt settings we need to have a HCI dev flag for it. This patch adds the flag and makes sure its value is changed whenever a mgmt_set_fast_connectable command completes. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e89938e0233..b6a33c5e768 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -410,6 +410,9 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_CONNECTABLE; + if (test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + settings |= MGMT_SETTING_FAST_CONNECTABLE; + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) settings |= MGMT_SETTING_DISCOVERABLE; @@ -2913,6 +2916,13 @@ static void fast_connectable_complete(struct hci_dev *hdev, u8 status) cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, mgmt_status(status)); } else { + struct mgmt_mode *cp = cmd->param; + + if (cp->val) + set_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + else + clear_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags); + send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); new_settings(hdev, cmd->sk); } @@ -2959,6 +2969,12 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (!!cp->val == test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) { + err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, + hdev); + goto unlock; + } + if (cp->val) { type = PAGE_SCAN_TYPE_INTERLACED; -- cgit v1.2.3 From 406d78045d6c3f5912aefe69b9b02e96479d51c8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:09 -0500 Subject: Bluetooth: Refactor fast connectable HCI commands This patch refactors the fast connectable HCI commands into their own HCI function. This is necessary so that the same function can be reused fo the fast connectable change required by disabling the connectable setting. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b6a33c5e768..f03b10cf92e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1000,6 +1000,29 @@ failed: return err; } +static void write_fast_connectable(struct hci_request *req, bool enable) +{ + struct hci_cp_write_page_scan_activity acp; + u8 type; + + if (enable) { + type = PAGE_SCAN_TYPE_INTERLACED; + + /* 160 msec page scan interval */ + acp.interval = __constant_cpu_to_le16(0x0100); + } else { + type = PAGE_SCAN_TYPE_STANDARD; /* default */ + + /* default 1.28 sec page scan */ + acp.interval = __constant_cpu_to_le16(0x0800); + } + + acp.window = __constant_cpu_to_le16(0x0012); + + hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), &acp); + hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); +} + static void set_connectable_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; @@ -2937,10 +2960,8 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; - struct hci_cp_write_page_scan_activity acp; struct pending_cmd *cmd; struct hci_request req; - u8 type; int err; BT_DBG("%s", hdev->name); @@ -2975,21 +2996,6 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (cp->val) { - type = PAGE_SCAN_TYPE_INTERLACED; - - /* 160 msec page scan interval */ - acp.interval = __constant_cpu_to_le16(0x0100); - } else { - type = PAGE_SCAN_TYPE_STANDARD; /* default */ - - /* default 1.28 sec page scan */ - acp.interval = __constant_cpu_to_le16(0x0800); - } - - /* default 11.25 msec page scan window */ - acp.window = __constant_cpu_to_le16(0x0012); - cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev, data, len); if (!cmd) { @@ -2999,8 +3005,7 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, hci_req_init(&req, hdev); - hci_req_add(&req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), &acp); - hci_req_add(&req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + write_fast_connectable(&req, cp->val); err = hci_req_run(&req, fast_connectable_complete); if (err < 0) { -- cgit v1.2.3 From e36a37691e53b54edb78209757fab0dd76c4614f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:10 -0500 Subject: Bluetooth: Disable fast connectable when disabling connectable When the connectable setting is disabled the fast connectable setting must also be disabled. This is so that we're consistent with the pre-requisites for enabling fast connectable, one of which is that the connectable setting is enabled. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f03b10cf92e..98f6295edbe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1119,6 +1119,9 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); + if (!cp->val && test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + write_fast_connectable(&req, false); + err = hci_req_run(&req, set_connectable_complete); if (err < 0) mgmt_pending_remove(cmd); -- cgit v1.2.3 From f332ec6699980e0563408c7bcf1a8a31b825fee1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:11 -0500 Subject: Bluetooth: Add reading of page scan parameters These parameters are related to the "fast connectable" mode that can be changed through the mgmt interface. Not all controllers properly reset these values with HCI_Reset so they need to be read in order to be able to verify whether the values are correct or not before enabling page scan. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_core.c | 6 ++++++ net/bluetooth/hci_event.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0ffd3587117..cfcad5423f1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -272,6 +272,12 @@ static void bredr_setup(struct hci_request *req) bacpy(&cp.bdaddr, BDADDR_ANY); cp.delete_all = 0x01; hci_req_add(req, HCI_OP_DELETE_STORED_LINK_KEY, sizeof(cp), &cp); + + /* Read page scan parameters */ + if (req->hdev->hci_ver > BLUETOOTH_VER_1_1) { + hci_req_add(req, HCI_OP_READ_PAGE_SCAN_ACTIVITY, 0, NULL); + hci_req_add(req, HCI_OP_READ_PAGE_SCAN_TYPE, 0, NULL); + } } static void le_setup(struct hci_request *req) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 84edacbc14a..3c6d0a4f78d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -601,6 +601,30 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) bacpy(&hdev->bdaddr, &rp->bdaddr); } +static void hci_cc_read_page_scan_activity(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_page_scan_activity *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) { + hdev->page_scan_interval = __le16_to_cpu(rp->interval); + hdev->page_scan_window = __le16_to_cpu(rp->window); + } +} + +static void hci_cc_read_page_scan_type(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_rp_read_page_scan_type *rp = (void *) skb->data; + + BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); + + if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) + hdev->page_scan_type = rp->type; +} + static void hci_cc_read_data_block_size(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2204,6 +2228,14 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_bd_addr(hdev, skb); break; + case HCI_OP_READ_PAGE_SCAN_ACTIVITY: + hci_cc_read_page_scan_activity(hdev, skb); + break; + + case HCI_OP_READ_PAGE_SCAN_TYPE: + hci_cc_read_page_scan_type(hdev, skb); + break; + case HCI_OP_READ_DATA_BLOCK_SIZE: hci_cc_read_data_block_size(hdev, skb); break; -- cgit v1.2.3 From 4a3ee763ba797e0489b7e9fd8810ae087c2a7504 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:12 -0500 Subject: Bluetooth: Update page scan parameters after successful write commands The page scan parameters (interval, window and type) stored in struct hci_dev should not only be updated after successful reads but also after successful writes. This patch adds the necessary handlers for the write command complete events and updates the stored values through them. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/hci_event.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'net/bluetooth') diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3c6d0a4f78d..138580745c2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -614,6 +614,25 @@ static void hci_cc_read_page_scan_activity(struct hci_dev *hdev, } } +static void hci_cc_write_page_scan_activity(struct hci_dev *hdev, + struct sk_buff *skb) +{ + u8 status = *((u8 *) skb->data); + struct hci_cp_write_page_scan_activity *sent; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY); + if (!sent) + return; + + hdev->page_scan_interval = __le16_to_cpu(sent->interval); + hdev->page_scan_window = __le16_to_cpu(sent->window); +} + static void hci_cc_read_page_scan_type(struct hci_dev *hdev, struct sk_buff *skb) { @@ -625,6 +644,22 @@ static void hci_cc_read_page_scan_type(struct hci_dev *hdev, hdev->page_scan_type = rp->type; } +static void hci_cc_write_page_scan_type(struct hci_dev *hdev, + struct sk_buff *skb) +{ + u8 status = *((u8 *) skb->data); + u8 *type; + + BT_DBG("%s status 0x%2.2x", hdev->name, status); + + if (status) + return; + + type = hci_sent_cmd_data(hdev, HCI_OP_WRITE_PAGE_SCAN_TYPE); + if (type) + hdev->page_scan_type = *type; +} + static void hci_cc_read_data_block_size(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2232,10 +2267,18 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_page_scan_activity(hdev, skb); break; + case HCI_OP_WRITE_PAGE_SCAN_ACTIVITY: + hci_cc_write_page_scan_activity(hdev, skb); + break; + case HCI_OP_READ_PAGE_SCAN_TYPE: hci_cc_read_page_scan_type(hdev, skb); break; + case HCI_OP_WRITE_PAGE_SCAN_TYPE: + hci_cc_write_page_scan_type(hdev, skb); + break; + case HCI_OP_READ_DATA_BLOCK_SIZE: hci_cc_read_data_block_size(hdev, skb); break; -- cgit v1.2.3 From bd98b9966f915411a32ecee3fa434cb051167d8a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:13 -0500 Subject: Bluetooth: Fix updating page scan parameters when not necessary Now that the current page scan parameters are stored in struct hci_dev we should check against those values before sending new HCI commands to change them. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 98f6295edbe..7783b8d8e1d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1002,6 +1002,7 @@ failed: static void write_fast_connectable(struct hci_request *req, bool enable) { + struct hci_dev *hdev = req->hdev; struct hci_cp_write_page_scan_activity acp; u8 type; @@ -1019,8 +1020,13 @@ static void write_fast_connectable(struct hci_request *req, bool enable) acp.window = __constant_cpu_to_le16(0x0012); - hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, sizeof(acp), &acp); - hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); + if (__cpu_to_le16(hdev->page_scan_interval) != acp.interval || + __cpu_to_le16(hdev->page_scan_window) != acp.window) + hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_ACTIVITY, + sizeof(acp), &acp); + + if (hdev->page_scan_type != type) + hci_req_add(req, HCI_OP_WRITE_PAGE_SCAN_TYPE, 1, &type); } static void set_connectable_complete(struct hci_dev *hdev, u8 status) -- cgit v1.2.3 From 4c01f8b845238710ff4b6c7fa8148ca52613f199 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:14 -0500 Subject: Bluetooth: Fix fast connectable state when enabling page scan When powering on or enabling page scan we need to ensure that the page scan parameters are as they should be. This is because some controllers do not properly reset these values upon HCI_Reset. Since the write_scan_parameters function is now called from several new places it also checks for the >= 1.2 HCI version requirement before sending the commands. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7783b8d8e1d..75c9d9269d7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1006,6 +1006,9 @@ static void write_fast_connectable(struct hci_request *req, bool enable) struct hci_cp_write_page_scan_activity acp; u8 type; + if (hdev->hci_ver < BLUETOOTH_VER_1_2) + return; + if (enable) { type = PAGE_SCAN_TYPE_INTERLACED; @@ -1125,7 +1128,13 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); - if (!cp->val && test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) + /* If we're going from non-connectable to connectable or + * vice-versa when fast connectable is enabled ensure that fast + * connectable gets disabled. write_fast_connectable won't do + * anything if the page scan parameters are already what they + * should be. + */ + if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) write_fast_connectable(&req, false); err = hci_req_run(&req, set_connectable_complete); @@ -3287,6 +3296,12 @@ static void set_bredr_scan(struct hci_request *req) struct hci_dev *hdev = req->hdev; u8 scan = 0; + /* Ensure that fast connectable is disabled. This function will + * not do anything if the page scan parameters are already what + * they should be. + */ + write_fast_connectable(req, false); + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) scan |= SCAN_PAGE; if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) -- cgit v1.2.3 From 1707c60e5d0d4c82c0601d92f10e24e04d2cc599 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:15 -0500 Subject: Bluetooth: Simplify address parameters of user_pairing_resp() Instead of passing the bdaddr and bdaddr_type as separate parameters to user_pairing_resp it's simpler to just pass the original mgmt_addr_info struct which contains both values. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 75c9d9269d7..8587229ed1d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2300,7 +2300,7 @@ unlock: } static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type, u16 mgmt_op, + struct mgmt_addr_info *addr, u16 mgmt_op, u16 hci_op, __le32 passkey) { struct pending_cmd *cmd; @@ -2315,10 +2315,10 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, goto done; } - if (type == BDADDR_BREDR) - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, bdaddr); + if (addr->type == BDADDR_BREDR) + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &addr->bdaddr); else - conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, bdaddr); + conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &addr->bdaddr); if (!conn) { err = cmd_status(sk, hdev->id, mgmt_op, @@ -2326,7 +2326,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, goto done; } - if (type == BDADDR_LE_PUBLIC || type == BDADDR_LE_RANDOM) { + if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) { /* Continue with pairing via SMP */ err = smp_user_confirm_reply(conn, mgmt_op, passkey); @@ -2340,7 +2340,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, goto done; } - cmd = mgmt_pending_add(sk, mgmt_op, hdev, bdaddr, sizeof(*bdaddr)); + cmd = mgmt_pending_add(sk, mgmt_op, hdev, addr, sizeof(*addr)); if (!cmd) { err = -ENOMEM; goto done; @@ -2350,11 +2350,12 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { struct hci_cp_user_passkey_reply cp; - bacpy(&cp.bdaddr, bdaddr); + bacpy(&cp.bdaddr, &addr->bdaddr); cp.passkey = passkey; err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); } else - err = hci_send_cmd(hdev, hci_op, sizeof(*bdaddr), bdaddr); + err = hci_send_cmd(hdev, hci_op, sizeof(addr->bdaddr), + &addr->bdaddr); if (err < 0) mgmt_pending_remove(cmd); @@ -2371,7 +2372,7 @@ static int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, BT_DBG(""); - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, + return user_pairing_resp(sk, hdev, &cp->addr, MGMT_OP_PIN_CODE_NEG_REPLY, HCI_OP_PIN_CODE_NEG_REPLY, 0); } @@ -2387,7 +2388,7 @@ static int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY, MGMT_STATUS_INVALID_PARAMS); - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, + return user_pairing_resp(sk, hdev, &cp->addr, MGMT_OP_USER_CONFIRM_REPLY, HCI_OP_USER_CONFIRM_REPLY, 0); } @@ -2399,7 +2400,7 @@ static int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev, BT_DBG(""); - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, + return user_pairing_resp(sk, hdev, &cp->addr, MGMT_OP_USER_CONFIRM_NEG_REPLY, HCI_OP_USER_CONFIRM_NEG_REPLY, 0); } @@ -2411,7 +2412,7 @@ static int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, + return user_pairing_resp(sk, hdev, &cp->addr, MGMT_OP_USER_PASSKEY_REPLY, HCI_OP_USER_PASSKEY_REPLY, cp->passkey); } @@ -2423,7 +2424,7 @@ static int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, BT_DBG(""); - return user_pairing_resp(sk, hdev, &cp->addr.bdaddr, cp->addr.type, + return user_pairing_resp(sk, hdev, &cp->addr, MGMT_OP_USER_PASSKEY_NEG_REPLY, HCI_OP_USER_PASSKEY_NEG_REPLY, 0); } -- cgit v1.2.3 From feb94d3d13af7b724b353d82237ca6f503c98d62 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 15 Mar 2013 17:07:16 -0500 Subject: Bluetooth: Fix PIN/Confirm/Passkey response parameters The only valid mgmt response to these pairing related commands is a mgmt_cmd_complete and the returned parameters should contain the address and address type of the remote device. Signed-off-by: Johan Hedberg Acked-by: Marcel Holtmann Signed-off-by: Gustavo Padovan --- net/bluetooth/mgmt.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8587229ed1d..03e7e732215 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2310,8 +2310,9 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_POWERED); + err = cmd_complete(sk, hdev->id, mgmt_op, + MGMT_STATUS_NOT_POWERED, addr, + sizeof(*addr)); goto done; } @@ -2321,8 +2322,9 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &addr->bdaddr); if (!conn) { - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_NOT_CONNECTED); + err = cmd_complete(sk, hdev->id, mgmt_op, + MGMT_STATUS_NOT_CONNECTED, addr, + sizeof(*addr)); goto done; } @@ -2331,11 +2333,13 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, err = smp_user_confirm_reply(conn, mgmt_op, passkey); if (!err) - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_SUCCESS); + err = cmd_complete(sk, hdev->id, mgmt_op, + MGMT_STATUS_SUCCESS, addr, + sizeof(*addr)); else - err = cmd_status(sk, hdev->id, mgmt_op, - MGMT_STATUS_FAILED); + err = cmd_complete(sk, hdev->id, mgmt_op, + MGMT_STATUS_FAILED, addr, + sizeof(*addr)); goto done; } -- cgit v1.2.3 From 0227c7b56959cd8f5edd20b6a47db86fa553e91a Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 20 Mar 2013 20:23:37 +0800 Subject: Bluetooth: fix error return code in rfcomm_add_listener() Fix to return a negative error code from the error handling case instead of 0, as returned elsewhere in this function. Signed-off-by: Wei Yongjun Signed-off-by: Gustavo Padovan --- net/bluetooth/rfcomm/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net/bluetooth') diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c index ba93df2af71..ca957d34b0c 100644 --- a/net/bluetooth/rfcomm/core.c +++ b/net/bluetooth/rfcomm/core.c @@ -2004,8 +2004,10 @@ static int rfcomm_add_listener(bdaddr_t *ba) /* Add listening session */ s = rfcomm_session_add(sock, BT_LISTEN); - if (!s) + if (!s) { + err = -ENOMEM; goto failed; + } return 0; failed: -- cgit v1.2.3