Bluetooth: Defer SCO setup if mode change is pending
Subsystems (2)
Marcel Holtmann
maintainerPATCH 1/1 Bluetooth: Defer SCO setup if mode change is pending
--- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++++--- net/bluetooth/hci_event.c | 43 ++++++++++++++++--------------------- 3 files changed, 49 insertions(+), 28 deletions(-)
include/net/bluetooth/hci_core.h +2 -0
@@ -256,6 +256,7 @@ enum {HCI_CONN_ENCRYPT_PEND ,HCI_CONN_RSWITCH_PEND ,HCI_CONN_MODE_CHANGE_PEND , + HCI_CONN_SCO_SETUP_PEND, };static inline void hci_conn_hash_init (struct hci_dev *hdev)
@@ -336,6 +337,7 @@ void hci_acl_connect(struct hci_conn *conn);void hci_acl_disconn (struct hci_conn *conn,__u8 reason);void hci_add_sco (struct hci_conn *conn,__u16 handle);void hci_setup_sync (struct hci_conn *conn,__u16 handle); +void hci_sco_setup (struct hci_conn *conn,__u8 status);struct hci_conn *hci_conn_add (struct hci_dev *hdev,int type,bdaddr_t *dst);int hci_conn_del (struct hci_conn *conn);
net/bluetooth/hci_conn.c +28 -4
@@ -155,6 +155,27 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)hci_send_cmd (hdev,HCI_OP_SETUP_SYNC_CONN ,sizeof (cp), &cp); } +/* Device _must_ be locked */ +void hci_sco_setup (struct hci_conn *conn,__u8 status) +{ +struct hci_conn *sco = conn->link; + +BT_DBG ("%p" , conn); + +if (!sco) +return ; + +if (!status) { +if (lmp_esco_capable (conn->hdev)) +hci_setup_sync (sco, conn->handle); +else +hci_add_sco (sco, conn->handle); + }else { +hci_proto_connect_cfm (sco, status); +hci_conn_del (sco); + } +} +static void hci_conn_timeout (unsigned long arg) {struct hci_conn *conn = (void *) arg;
@@ -385,10 +406,13 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl->power_save =1 ;hci_conn_enter_active_mode (acl); -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, acl->handle); -else -hci_add_sco (sco, acl->handle); +if (test_bit (HCI_CONN_MODE_CHANGE_PEND , &acl->pend)) { +/* defer SCO setup until mode change completed */ +set_bit (HCI_CONN_SCO_SETUP_PEND , &acl->pend); +return sco; + } + +hci_sco_setup (acl,0x00 ); }return sco;
net/bluetooth/hci_event.c +19 -24
79 lines not on this page · raw message
Signature · 1 line
1.7.1.1
Marcel:
Quoted 6 lines · Marcel Holtmann
net/bluetooth/hci_conn.c +0
-2@@ -385,10 +406,13 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl->power_save =1 ;hci_conn_enter_active_mode (acl); -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, acl->handle);
net/bluetooth/hci_conn.c struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 · line 416
Quoted 10 lines
net/bluetooth/hci_conn.c +7
-2-else -hci_add_sco (sco, acl->handle); +if (test_bit (HCI_CONN_MODE_CHANGE_PEND , &acl->pend)) { +/* defer SCO setup until mode change completed */ +set_bit (HCI_CONN_SCO_SETUP_PEND , &acl->pend); +return sco; + } + +hci_sco_setup (acl,0x00 ); }
Not in love with the "fake" status passed here, but the overall change to centralize this functionality provides a nice cleanup from the original patch and makes the code more readable and maintainable.
Quoted 18 lines
net/bluetooth/hci_conn.c +0
-0return sco;net/bluetooth/hci_event.c +0
-3@@ -775,9 +775,6 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_SNIFF_MODE );if (!cp)return ;@@ -785,8 +782,13 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev);
net/bluetooth/hci_event.c static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) · line 790
Quoted 10 lines
net/bluetooth/hci_event.c +6
-2conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) +clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , &conn->pend)) +hci_sco_setup (conn, status);
Probably the subject for another patch, but has there been any reported issue where idle_timeout is set short enough that the timer expires after exit sniff mode has been requested but before the mode change event has been received?
Got back from other duties late tonight, so I didn't get testing fully finished. In particular, I need to see what happens when idle_timeout is set very low ex < 10. I'll let you know the results first thing in the morning. If everything is good, I'll regenerate the patch with original hcidump log included
Signature · 3 lines
Ron Shaffer Employee of the Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Marcel Holtmann
maintainerHi Ron,
Quoted 4 lines
Quoted 4 lines
net/bluetooth/hci_conn.c +0
-0@@ -385,10 +406,13 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl->power_save =1 ;hci_conn_enter_active_mode (acl);
Quoted 16 lines
net/bluetooth/hci_conn.c struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 · line 416
Quoted 12 lines
net/bluetooth/hci_conn.c +7
-4-if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, acl->handle); -else -hci_add_sco (sco, acl->handle); +if (test_bit (HCI_CONN_MODE_CHANGE_PEND , &acl->pend)) { +/* defer SCO setup until mode change completed */ +set_bit (HCI_CONN_SCO_SETUP_PEND , &acl->pend); +return sco; + } + +hci_sco_setup (acl,0x00 ); }Not in love with the "fake" status passed here, but the overall change to centralize this functionality provides a nice cleanup from the original patch and makes the code more readable and maintainable.
yeah, the 0x00 looks funny here. We could add a comment explaining it, but it does make sense in the end.
Quoted 18 lines
Quoted 18 lines
net/bluetooth/hci_conn.c +0
-0return sco;net/bluetooth/hci_event.c +0
-3@@ -775,9 +775,6 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_SNIFF_MODE );if (!cp)return ;@@ -785,8 +782,13 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev);
Quoted 14 lines
net/bluetooth/hci_event.c static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) · line 790
Quoted 9 lines
net/bluetooth/hci_event.c +6
-2conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) +clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , &conn->pend)) +hci_sco_setup (conn, status);Probably the subject for another patch, but has there been any reported issue where
idle_timeoutis set short enough that the timer expires after exit sniff mode has been requested but before the mode change event has been received?
I see that this could potentially happen. The timer is not suppose to be running during that transaction. It should be only started when the mode change has been completed. That needs fixing, but not in this patch.
Regards
Marcel
PATCH v2 1/1 Bluetooth: Defer SCO setup if mode change is pending
From: Marcel Holtmann <[email protected]>
Certain headsets such as the Motorola H350 will reject SCO and eSCO connection requests while the ACL is transitioning from sniff mode to active mode. Add synchronization so that SCO and eSCO connection requests will wait until the ACL has fully transitioned to active mode.
< HCI Command: Exit Sniff Mode (0x02|0x0004) plen 2
handle 12
Quoted 1 line
HCI Event: Command Status (
0x0f) plen 4
Exit Sniff Mode (0x02|0x0004) status 0x00 ncmd 1
< HCI Command: Setup Synchronous Connection (0x01|0x0028) plen 17
handle 12 voice setting 0x0040Quoted 1 line
HCI Event: Command Status (
0x0f) plen 4
Setup Synchronous Connection (0x01|0x0028) status 0x00 ncmd 1
Quoted 1 line
HCI Event: Number of Completed Packets (
0x13) plen 5
handle 12 packets 1
Quoted 1 line
HCI Event: Mode Change (
0x14) plen 6
status 0x00 handle 12 mode 0x00 interval 0
Mode: ActiveQuoted 1 line
HCI Event: Synchronous Connect Complete (
0x2c) plen 17
status 0x10 handle 14 bdaddr 00:1A:0E:50:28:A4 type SCO
Error: Connection Accept Timeout Exceeded
--- include/net/bluetooth/hci_core.h | 2 + net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++--- net/bluetooth/hci_event.c | 47 ++++++++++++++++++------------------- 3 files changed, 53 insertions(+), 28 deletions(-)
include/net/bluetooth/hci_core.h +2 -0
@@ -250,6 +250,7 @@ enum {HCI_CONN_ENCRYPT_PEND ,HCI_CONN_RSWITCH_PEND ,HCI_CONN_MODE_CHANGE_PEND , + HCI_CONN_SCO_SETUP_PEND, };static inline void hci_conn_hash_init (struct hci_dev *hdev)
@@ -330,6 +331,7 @@ void hci_acl_connect(struct hci_conn *conn);void hci_acl_disconn (struct hci_conn *conn,__u8 reason);void hci_add_sco (struct hci_conn *conn,__u16 handle);void hci_setup_sync (struct hci_conn *conn,__u16 handle); +void hci_sco_setup (struct hci_conn *conn,__u8 status);struct hci_conn *hci_conn_add (struct hci_dev *hdev,int type,bdaddr_t *dst);int hci_conn_del (struct hci_conn *conn);
net/bluetooth/hci_conn.c +28 -4
@@ -155,6 +155,27 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)hci_send_cmd (hdev,HCI_OP_SETUP_SYNC_CONN ,sizeof (cp), &cp); } +/* Device _must_ be locked */ +void hci_sco_setup (struct hci_conn *conn,__u8 status) +{ +struct hci_conn *sco = conn->link; + +BT_DBG ("%p" , conn); + +if (!sco) +return ; + +if (!status) { +if (lmp_esco_capable (conn->hdev)) +hci_setup_sync (sco, conn->handle); +else +hci_add_sco (sco, conn->handle); + }else { +hci_proto_connect_cfm (sco, status); +hci_conn_del (sco); + } +} +static void hci_conn_timeout (unsigned long arg) {struct hci_conn *conn = (void *) arg;
@@ -380,10 +401,13 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl->power_save =1 ;hci_conn_enter_active_mode (acl); -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, acl->handle); -else -hci_add_sco (sco, acl->handle); +if (test_bit (HCI_CONN_MODE_CHANGE_PEND , &acl->pend)) { +/* defer SCO setup until mode change completed */ +set_bit (HCI_CONN_SCO_SETUP_PEND , &acl->pend); +return sco; + } + +hci_sco_setup (acl,0x00 ); }return sco;
net/bluetooth/hci_event.c +23 -24
83 lines not on this page · raw message
Signature · 5 lines
1.7.0.2 -- Ron Shaffer Employee of the Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Marcel Holtmann
maintainerHi Ron,
Quoted 22 lines
net/bluetooth/hci_event.c +2
-5@@ -775,9 +775,6 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_SNIFF_MODE );if (!cp)return ;@@ -785,8 +782,15 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) {
net/bluetooth/hci_event.c static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) · line 796
Quoted 10 lines
net/bluetooth/hci_event.c +7
-0+clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } + }hci_dev_unlock (hdev); }
this change now makes it worse, please revert to initial
if (!status) return;
check at the top and the just do
if (conn) {
clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend);
if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend))
hci_sco_setup(conn, status);
}The only reason why I moved the status check down, because I was not thinking straight and overlooked that in case of success we don't wanna do a single thing here.
Quoted 18 lines
net/bluetooth/hci_event.c +2
-5@@ -798,9 +802,6 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_EXIT_SNIFF_MODE );if (!cp)return ;@@ -808,8 +809,15 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) {
net/bluetooth/hci_event.c static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) · line 823
Quoted 10 lines
net/bluetooth/hci_event.c +7
-0+clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } + }hci_dev_unlock (hdev); }
Same applies here btw.
Quoted 24 lines
net/bluetooth/hci_event.c +2
-14@@ -915,20 +923,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s }else conn->state =BT_CLOSED ; -if (conn->type ==ACL_LINK ) { -struct hci_conn *sco = conn->link; -if (sco) { -if (!ev->status) { -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, conn->handle); -else -hci_add_sco (sco, conn->handle); - }else { -hci_proto_connect_cfm (sco, ev->status); -hci_conn_del (sco); - } - } - } +if (conn->type ==ACL_LINK ) +hci_sco_setup (conn, ev->status);if (ev->status) {hci_proto_connect_cfm (conn, ev->status);
net/bluetooth/hci_event.c static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb · line 1481
Quoted 8 lines
net/bluetooth/hci_event.c +3
-0@@ -1478,6 +1474,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb conn->power_save =1 ; else conn->power_save = 0; + }else if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) { +hci_sco_setup (conn, ev->status); } }
Please don't do else if here. Just add a separate check like I had in my initial patch. We don't wanna depend on two flags pending flags set here. If the SCO setup is pending, then we execute it. No matter how we reached that point. It is important that even if we receive a mode change without us triggering it, that we clear any potential SCO pending flag.
Regards
Marcel
Marcel:
Quoted 49 lines
Hi Ron,
Quoted 22 lines
net/bluetooth/hci_event.c +2
-5@@ -775,9 +775,6 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_SNIFF_MODE );if (!cp)return ;@@ -785,8 +782,15 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) {net/bluetooth/hci_event.c static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) · line 796
Quoted 10 lines
net/bluetooth/hci_event.c +7
-0+clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } + }hci_dev_unlock (hdev); }this change now makes it worse, please revert to initial
if (!status) return;check at the top and the just do
if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) hci_sco_setup(conn, status); }
Quoted 3 lines
The only reason why I moved the status check down, because I was not thinking straight and overlooked that in case of success we don't wanna do a single thing here.
Makes sense. Done.
Quoted 27 lines
Quoted 27 lines
net/bluetooth/hci_event.c +9
-5@@ -798,9 +802,6 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_EXIT_SNIFF_MODE );if (!cp)return ;@@ -808,8 +809,15 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) { +clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } + }
Quoted 4 lines
net/bluetooth/hci_event.c static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) · line 823
Quoted 2 lines
net/bluetooth/hci_event.c +0
-0hci_dev_unlock (hdev); }Same applies here btw.
Same. Done.
Quoted 32 lines
Quoted 32 lines
net/bluetooth/hci_event.c +5
-14@@ -915,20 +923,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s }else conn->state =BT_CLOSED ; -if (conn->type ==ACL_LINK ) { -struct hci_conn *sco = conn->link; -if (sco) { -if (!ev->status) { -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, conn->handle); -else -hci_add_sco (sco, conn->handle); - }else { -hci_proto_connect_cfm (sco, ev->status); -hci_conn_del (sco); - } - } - } +if (conn->type ==ACL_LINK ) +hci_sco_setup (conn, ev->status);if (ev->status) {hci_proto_connect_cfm (conn, ev->status);@@ -1478,6 +1474,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb conn->power_save =1 ; else conn->power_save = 0; + }else if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) { +hci_sco_setup (conn, ev->status); }
Quoted 8 lines
net/bluetooth/hci_event.c static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb · line 1481
Quoted 1 line
net/bluetooth/hci_event.c +0
-0}Please don't do else if here. Just add a separate check like I had in my initial patch. We don't wanna depend on two flags pending flags set here. If the SCO setup is pending, then we execute it. No matter how we reached that point. It is important that even if we receive a mode change without us triggering it, that we clear any potential SCO pending flag.
Hmmm. I know I found a bug here, but I can't remember how it manifested itself. Today, I can't reproduce it, so either I was seeing things or I'm losing it.
I'll revert it back, and if I run across the issue again, we can fix it later.
Signature · 3 lines
Ron Shaffer Employee of the Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Marcel Holtmann
maintainerHi Ron,
Quoted 128 lines
Quoted 47 lines
Quoted 22 lines
net/bluetooth/hci_event.c +2
-5@@ -775,9 +775,6 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_SNIFF_MODE );if (!cp)return ;@@ -785,8 +782,15 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) {net/bluetooth/hci_event.c static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status) · line 796
Quoted 10 lines
net/bluetooth/hci_event.c +7
-0+clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } + }hci_dev_unlock (hdev); }this change now makes it worse, please revert to initial
if (!status) return;check at the top and the just do
if (conn) { clear_bit(HCI_CONN_MODE_CHANGE_PEND, &conn->pend); if (test_and_clear_bit(HCI_CONN_SCO_SETUP_PEND, &conn->pend)) hci_sco_setup(conn, status); }Quoted 3 lines
The only reason why I moved the status check down, because I was not thinking straight and overlooked that in case of success we don't wanna do a single thing here.
Makes sense. Done.
Quoted 27 lines
Quoted 27 lines
net/bluetooth/hci_event.c +9
-5@@ -798,9 +802,6 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)BT_DBG ("%s status 0x%x" , hdev->name, status); -if (!status) -return ; - cp =hci_sent_cmd_data (hdev,HCI_OP_EXIT_SNIFF_MODE );if (!cp)return ;@@ -808,8 +809,15 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) -clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (conn) { +if (status) { +clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } + }Quoted 4 lines
net/bluetooth/hci_event.c static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status) · line 823
Quoted 2 lines
net/bluetooth/hci_event.c +0
-0hci_dev_unlock (hdev); }Same applies here btw.
Same. Done.
Quoted 32 lines
Quoted 32 lines
net/bluetooth/hci_event.c +5
-14@@ -915,20 +923,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s }else conn->state =BT_CLOSED ; -if (conn->type ==ACL_LINK ) { -struct hci_conn *sco = conn->link; -if (sco) { -if (!ev->status) { -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, conn->handle); -else -hci_add_sco (sco, conn->handle); - }else { -hci_proto_connect_cfm (sco, ev->status); -hci_conn_del (sco); - } - } - } +if (conn->type ==ACL_LINK ) +hci_sco_setup (conn, ev->status);if (ev->status) {hci_proto_connect_cfm (conn, ev->status);@@ -1478,6 +1474,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb conn->power_save =1 ; else conn->power_save = 0; + }else if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) { +hci_sco_setup (conn, ev->status); }Quoted 8 lines
net/bluetooth/hci_event.c static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb · line 1481
Quoted 1 line
net/bluetooth/hci_event.c +0
-0}Please don't do else if here. Just add a separate check like I had in my initial patch. We don't wanna depend on two flags pending flags set here. If the SCO setup is pending, then we execute it. No matter how we reached that point. It is important that even if we receive a mode change without us triggering it, that we clear any potential SCO pending flag.
Quoted 6 lines
Hmmm. I know I found a bug here, but I can't remember how it manifested itself. Today, I can't reproduce it, so either I was seeing things or I'm losing it.
I'll revert it back, and if I run across the issue again, we can fix it later.
please send v3 of this patch then.
Regards
Marcel
PATCH v3 1/1 Bluetooth: Defer SCO setup if mode change is pending
From: Marcel Holtmann <[email protected]>
Certain headsets such as the Motorola H350 will reject SCO and eSCO connection requests while the ACL is transitioning from sniff mode to active mode. Add synchronization so that SCO and eSCO connection requests will wait until the ACL has fully transitioned to active mode.
< HCI Command: Exit Sniff Mode (0x02|0x0004) plen 2
handle 12
Quoted 1 line
HCI Event: Command Status (
0x0f) plen 4
Exit Sniff Mode (0x02|0x0004) status 0x00 ncmd 1
< HCI Command: Setup Synchronous Connection (0x01|0x0028) plen 17
handle 12 voice setting 0x0040Quoted 1 line
HCI Event: Command Status (
0x0f) plen 4
Setup Synchronous Connection (0x01|0x0028) status 0x00 ncmd 1
Quoted 1 line
HCI Event: Number of Completed Packets (
0x13) plen 5
handle 12 packets 1
Quoted 1 line
HCI Event: Mode Change (
0x14) plen 6
status 0x00 handle 12 mode 0x00 interval 0
Mode: ActiveQuoted 1 line
HCI Event: Synchronous Connect Complete (
0x2c) plen 17
status 0x10 handle 14 bdaddr 00:1A:0E:50:28:A4 type SCO
Error: Connection Accept Timeout Exceeded
--- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++++++++---- net/bluetooth/hci_event.c | 33 +++++++++++++++++---------------- 3 files changed, 47 insertions(+), 20 deletions(-)
include/net/bluetooth/hci_core.h +2 -0
@@ -250,6 +250,7 @@ enum {HCI_CONN_ENCRYPT_PEND ,HCI_CONN_RSWITCH_PEND ,HCI_CONN_MODE_CHANGE_PEND , + HCI_CONN_SCO_SETUP_PEND, };static inline void hci_conn_hash_init (struct hci_dev *hdev)
@@ -330,6 +331,7 @@ void hci_acl_connect(struct hci_conn *conn);void hci_acl_disconn (struct hci_conn *conn,__u8 reason);void hci_add_sco (struct hci_conn *conn,__u16 handle);void hci_setup_sync (struct hci_conn *conn,__u16 handle); +void hci_sco_setup (struct hci_conn *conn,__u8 status);struct hci_conn *hci_conn_add (struct hci_dev *hdev,int type,bdaddr_t *dst);int hci_conn_del (struct hci_conn *conn);
net/bluetooth/hci_conn.c +28 -4
@@ -155,6 +155,27 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)hci_send_cmd (hdev,HCI_OP_SETUP_SYNC_CONN ,sizeof (cp), &cp); } +/* Device _must_ be locked */ +void hci_sco_setup (struct hci_conn *conn,__u8 status) +{ +struct hci_conn *sco = conn->link; + +BT_DBG ("%p" , conn); + +if (!sco) +return ; + +if (!status) { +if (lmp_esco_capable (conn->hdev)) +hci_setup_sync (sco, conn->handle); +else +hci_add_sco (sco, conn->handle); + }else { +hci_proto_connect_cfm (sco, status); +hci_conn_del (sco); + } +} +static void hci_conn_timeout (unsigned long arg) {struct hci_conn *conn = (void *) arg;
@@ -380,10 +401,13 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 acl->power_save =1 ;hci_conn_enter_active_mode (acl); -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, acl->handle); -else -hci_add_sco (sco, acl->handle); +if (test_bit (HCI_CONN_MODE_CHANGE_PEND , &acl->pend)) { +/* defer SCO setup until mode change completed */ +set_bit (HCI_CONN_SCO_SETUP_PEND , &acl->pend); +return sco; + } + +hci_sco_setup (acl,0x00 ); }return sco;
net/bluetooth/hci_event.c +17 -16
@@ -785,9 +785,14 @@ static void hci_cs_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) +if (conn) {clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } +hci_dev_unlock (hdev); }
@@ -808,9 +813,14 @@ static void hci_cs_exit_sniff_mode(struct hci_dev *hdev, __u8 status)hci_dev_lock (hdev); conn =hci_conn_hash_lookup_handle (hdev,__le16_to_cpu (cp->handle)); -if (conn) +if (conn) {clear_bit (HCI_CONN_MODE_CHANGE_PEND , &conn->pend); +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , + &conn->pend)) +hci_sco_setup (conn, status); + } +hci_dev_unlock (hdev); }
@@ -915,20 +925,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s }else conn->state =BT_CLOSED ; -if (conn->type ==ACL_LINK ) { -struct hci_conn *sco = conn->link; -if (sco) { -if (!ev->status) { -if (lmp_esco_capable (hdev)) -hci_setup_sync (sco, conn->handle); -else -hci_add_sco (sco, conn->handle); - }else { -hci_proto_connect_cfm (sco, ev->status); -hci_conn_del (sco); - } - } - } +if (conn->type ==ACL_LINK ) +hci_sco_setup (conn, ev->status);if (ev->status) {hci_proto_connect_cfm (conn, ev->status);
@@ -1479,6 +1477,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skbelse conn->power_save =0 ; } + +if (test_and_clear_bit (HCI_CONN_SCO_SETUP_PEND , &conn->pend)) +hci_sco_setup (conn, ev->status); }hci_dev_unlock (hdev);
Signature · 5 lines
1.7.0.2 -- Ron Shaffer Employee of the Qualcomm Innovation Center, Inc. Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Marcel Holtmann
maintainerHi Ron,
Quoted 22 lines
Certain headsets such as the Motorola H350 will reject SCO and eSCO connection requests while the ACL is transitioning from sniff mode to active mode. Add synchronization so that SCO and eSCO connection requests will wait until the ACL has fully transitioned to active mode.
< HCI Command: Exit Sniff Mode (0x02|0x0004) plen 2
handle 12Quoted 1 line
HCI Event: Command Status (
0x0f) plen 4Exit Sniff Mode (0x02|0x0004) status 0x00 ncmd 1 < HCI Command: Setup Synchronous Connection (0x01|0x0028) plen 17 handle 12 voice setting 0x0040Quoted 1 line
HCI Event: Command Status (
0x0f) plen 4Setup Synchronous Connection (0x01|0x0028) status 0x00 ncmd 1Quoted 1 line
HCI Event: Number of Completed Packets (
0x13) plen 5handle 12 packets 1
Quoted 1 line
HCI Event: Mode Change (
0x14) plen 6status 0x00 handle 12 mode 0x00 interval 0 Mode: ActiveQuoted 1 line
HCI Event: Synchronous Connect Complete (
0x2c) plen 17status
0x10handle 14 bdaddr 00:1A:0E:50:28:A4 type SCO
Error: Connection Accept Timeout Exceeded
Quoted 7 lines
Signed-off-by Marcel Holtmann <[email protected]>Signed-off-by Ron Shaffer <[email protected]>--- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++++++++---- net/bluetooth/hci_event.c | 33 +++++++++++++++++---------------- 3 files changed, 47 insertions(+), 20 deletions(-)
I did a small cleanup, but patch has been applied now.
Regards
Marcel