From e73439d8c0e4c522c843b8bb98c0eb5700da6b05 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 26 Jul 2010 10:06:00 -0400 Subject: [PATCH 1/7] Bluetooth: Defer SCO setup if mode change is pending 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 > 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 0x0040 > HCI Event: Command Status (0x0f) plen 4 Setup Synchronous Connection (0x01|0x0028) status 0x00 ncmd 1 > HCI Event: Number of Completed Packets (0x13) plen 5 handle 12 packets 1 > HCI Event: Mode Change (0x14) plen 6 status 0x00 handle 12 mode 0x00 interval 0 Mode: Active > 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 Signed-off-by: Ron Shaffer Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 32 ++++++++++++++++++++++++++++---- net/bluetooth/hci_event.c | 31 +++++++++++++++---------------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 350b3e6964bd..8b28962e737e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -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); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e9fef83449f8..0b1e460fe440 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -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; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2069c3b05fda..bfef5bae0b3a 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -785,9 +785,13 @@ 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 +812,13 @@ 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 +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); @@ -1481,6 +1477,9 @@ static inline void hci_mode_change_evt(struct hci_dev *hdev, struct sk_buff *skb else 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); From 0bbdf6cba0fb730ae2f2cfd5ead3d1e2e5498ddc Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 24 Jul 2010 01:06:05 -0300 Subject: [PATCH 2/7] Bluetooth: Fix permission of hci_ath.c .c file shall not have the 'x' permission. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ath.c | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 drivers/bluetooth/hci_ath.c diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c old mode 100755 new mode 100644 From da5f6c37eee040775997191d1a1bc91c0c1e51eb Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 24 Jul 2010 01:34:54 -0300 Subject: [PATCH 3/7] Bluetooth: Test 'count' value before enter the loop Testing first we avoid enter the loop when count = 0. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 995c9f9b84d0..8303f1c9ef54 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1149,7 +1149,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) if (type < HCI_ACLDATA_PKT || type > HCI_EVENT_PKT) return -EILSEQ; - do { + while (count) { rem = hci_reassembly(hdev, type, data, count, type - 1, GFP_ATOMIC); if (rem < 0) @@ -1157,7 +1157,7 @@ int hci_recv_fragment(struct hci_dev *hdev, int type, void *data, int count) data += (count - rem); count = rem; - } while (count); + }; return rem; } @@ -1170,7 +1170,7 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) int type; int rem = 0; - do { + while (count) { struct sk_buff *skb = hdev->reassembly[STREAM_REASSEMBLY]; if (!skb) { @@ -1192,7 +1192,7 @@ int hci_recv_stream_fragment(struct hci_dev *hdev, void *data, int count) data += (count - rem); count = rem; - } while (count); + }; return rem; } From e9da101f6d0c9a8fda9f78a80365ba2a9f75603f Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 24 Jul 2010 01:46:57 -0300 Subject: [PATCH 4/7] Bluetooth: Use hci_recv_stream_fragment() in UART driver Use the new hci_recv_stream_fragment() to reassembly incoming UART streams. Signed-off-by: Gustavo F. Padovan Tested-by: Ville Tervo Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h4.c | 103 +------------------------------------ 1 file changed, 2 insertions(+), 101 deletions(-) diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index 3f038f5308a4..b2cf50e3cafb 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -151,107 +151,8 @@ static inline int h4_check_data_len(struct h4_struct *h4, int len) /* Recv data */ static int h4_recv(struct hci_uart *hu, void *data, int count) { - struct h4_struct *h4 = hu->priv; - register char *ptr; - struct hci_event_hdr *eh; - struct hci_acl_hdr *ah; - struct hci_sco_hdr *sh; - register int len, type, dlen; - - BT_DBG("hu %p count %d rx_state %ld rx_count %ld", - hu, count, h4->rx_state, h4->rx_count); - - ptr = data; - while (count) { - if (h4->rx_count) { - len = min_t(unsigned int, h4->rx_count, count); - memcpy(skb_put(h4->rx_skb, len), ptr, len); - h4->rx_count -= len; count -= len; ptr += len; - - if (h4->rx_count) - continue; - - switch (h4->rx_state) { - case H4_W4_DATA: - BT_DBG("Complete data"); - - hci_recv_frame(h4->rx_skb); - - h4->rx_state = H4_W4_PACKET_TYPE; - h4->rx_skb = NULL; - continue; - - case H4_W4_EVENT_HDR: - eh = hci_event_hdr(h4->rx_skb); - - BT_DBG("Event header: evt 0x%2.2x plen %d", eh->evt, eh->plen); - - h4_check_data_len(h4, eh->plen); - continue; - - case H4_W4_ACL_HDR: - ah = hci_acl_hdr(h4->rx_skb); - dlen = __le16_to_cpu(ah->dlen); - - BT_DBG("ACL header: dlen %d", dlen); - - h4_check_data_len(h4, dlen); - continue; - - case H4_W4_SCO_HDR: - sh = hci_sco_hdr(h4->rx_skb); - - BT_DBG("SCO header: dlen %d", sh->dlen); - - h4_check_data_len(h4, sh->dlen); - continue; - } - } - - /* H4_W4_PACKET_TYPE */ - switch (*ptr) { - case HCI_EVENT_PKT: - BT_DBG("Event packet"); - h4->rx_state = H4_W4_EVENT_HDR; - h4->rx_count = HCI_EVENT_HDR_SIZE; - type = HCI_EVENT_PKT; - break; - - case HCI_ACLDATA_PKT: - BT_DBG("ACL packet"); - h4->rx_state = H4_W4_ACL_HDR; - h4->rx_count = HCI_ACL_HDR_SIZE; - type = HCI_ACLDATA_PKT; - break; - - case HCI_SCODATA_PKT: - BT_DBG("SCO packet"); - h4->rx_state = H4_W4_SCO_HDR; - h4->rx_count = HCI_SCO_HDR_SIZE; - type = HCI_SCODATA_PKT; - break; - - default: - BT_ERR("Unknown HCI packet type %2.2x", (__u8)*ptr); - hu->hdev->stat.err_rx++; - ptr++; count--; - continue; - }; - - ptr++; count--; - - /* Allocate packet */ - h4->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); - if (!h4->rx_skb) { - BT_ERR("Can't allocate mem for new packet"); - h4->rx_state = H4_W4_PACKET_TYPE; - h4->rx_count = 0; - return -ENOMEM; - } - - h4->rx_skb->dev = (void *) hu->hdev; - bt_cb(h4->rx_skb)->pkt_type = type; - } + if (hci_recv_stream_fragment(hu->hdev, data, count) < 0) + BT_ERR("Frame Reassembly Failed"); return count; } From f2b94bb9e0b8bd048331a6e9d616e918f4bcbd97 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 24 Jul 2010 02:04:44 -0300 Subject: [PATCH 5/7] Bluetooth: Add __init and __exit marks to UART drivers Those marks are useful to save space in the binary and in the memory. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ath.c | 4 ++-- drivers/bluetooth/hci_bcsp.c | 4 ++-- drivers/bluetooth/hci_h4.c | 4 ++-- drivers/bluetooth/hci_ll.c | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index 5ab258bccccb..b941dd5dc981 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -217,7 +217,7 @@ static struct hci_uart_proto athp = { .flush = ath_flush, }; -int ath_init(void) +int __init ath_init(void) { int err = hci_uart_register_proto(&athp); @@ -229,7 +229,7 @@ int ath_init(void) return err; } -int ath_deinit(void) +int __exit ath_deinit(void) { return hci_uart_unregister_proto(&athp); } diff --git a/drivers/bluetooth/hci_bcsp.c b/drivers/bluetooth/hci_bcsp.c index 42d69d4de05c..9c5b2dc38e29 100644 --- a/drivers/bluetooth/hci_bcsp.c +++ b/drivers/bluetooth/hci_bcsp.c @@ -739,7 +739,7 @@ static struct hci_uart_proto bcsp = { .flush = bcsp_flush }; -int bcsp_init(void) +int __init bcsp_init(void) { int err = hci_uart_register_proto(&bcsp); @@ -751,7 +751,7 @@ int bcsp_init(void) return err; } -int bcsp_deinit(void) +int __exit bcsp_deinit(void) { return hci_uart_unregister_proto(&bcsp); } diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c index b2cf50e3cafb..7b8ad93e2c36 100644 --- a/drivers/bluetooth/hci_h4.c +++ b/drivers/bluetooth/hci_h4.c @@ -173,7 +173,7 @@ static struct hci_uart_proto h4p = { .flush = h4_flush, }; -int h4_init(void) +int __init h4_init(void) { int err = hci_uart_register_proto(&h4p); @@ -185,7 +185,7 @@ int h4_init(void) return err; } -int h4_deinit(void) +int __exit h4_deinit(void) { return hci_uart_unregister_proto(&h4p); } diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c index 5744aba8272e..38595e782d02 100644 --- a/drivers/bluetooth/hci_ll.c +++ b/drivers/bluetooth/hci_ll.c @@ -517,7 +517,7 @@ static struct hci_uart_proto llp = { .flush = ll_flush, }; -int ll_init(void) +int __init ll_init(void) { int err = hci_uart_register_proto(&llp); @@ -529,7 +529,7 @@ int ll_init(void) return err; } -int ll_deinit(void) +int __exit ll_deinit(void) { return hci_uart_unregister_proto(&llp); } From 2f8362afcd2da8b313ec3cc04a50af19d3592972 Mon Sep 17 00:00:00 2001 From: "Gustavo F. Padovan" Date: Sat, 24 Jul 2010 02:04:45 -0300 Subject: [PATCH 6/7] Bluetooth: Add __init and __exit marks to RFCOMM Those annotation save memory and space on the binary. __init code is discarded just after execute and __exit code is discarded if the module is built into the kernel image or unload of modules is not allowed. Signed-off-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- net/bluetooth/rfcomm/sock.c | 2 +- net/bluetooth/rfcomm/tty.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c index 43fbf6b4b4bf..44a623275951 100644 --- a/net/bluetooth/rfcomm/sock.c +++ b/net/bluetooth/rfcomm/sock.c @@ -1152,7 +1152,7 @@ error: return err; } -void rfcomm_cleanup_sockets(void) +void __exit rfcomm_cleanup_sockets(void) { debugfs_remove(rfcomm_sock_debugfs); diff --git a/net/bluetooth/rfcomm/tty.c b/net/bluetooth/rfcomm/tty.c index 309b6c261b25..026205c18b78 100644 --- a/net/bluetooth/rfcomm/tty.c +++ b/net/bluetooth/rfcomm/tty.c @@ -1153,7 +1153,7 @@ static const struct tty_operations rfcomm_ops = { .tiocmset = rfcomm_tty_tiocmset, }; -int rfcomm_init_ttys(void) +int __init rfcomm_init_ttys(void) { rfcomm_tty_driver = alloc_tty_driver(RFCOMM_TTY_PORTS); if (!rfcomm_tty_driver) @@ -1183,7 +1183,7 @@ int rfcomm_init_ttys(void) return 0; } -void rfcomm_cleanup_ttys(void) +void __exit rfcomm_cleanup_ttys(void) { tty_unregister_driver(rfcomm_tty_driver); put_tty_driver(rfcomm_tty_driver); From 4ebaa4edf8799cab19d5a0642dc95f04fd284e06 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 23 Jul 2010 12:11:04 +0200 Subject: [PATCH 7/7] Bluetooth: Fix kfree() => kfree_skb() in hci_ath.c sk_buffs have to be freed with kfree_skb() instead of kfree(). Signed-off-by: Dan Carpenter Acked-by: Gustavo F. Padovan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_ath.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ath.c b/drivers/bluetooth/hci_ath.c index b941dd5dc981..6a160c17ea94 100644 --- a/drivers/bluetooth/hci_ath.c +++ b/drivers/bluetooth/hci_ath.c @@ -163,7 +163,7 @@ static int ath_enqueue(struct hci_uart *hu, struct sk_buff *skb) struct ath_struct *ath = hu->priv; if (bt_cb(skb)->pkt_type == HCI_SCODATA_PKT) { - kfree(skb); + kfree_skb(skb); return 0; }