diff options
Diffstat (limited to 'drivers/bluetooth/hci_h5.c')
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 66 |
1 files changed, 50 insertions, 16 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index d9d42f65ee6e..6fb8d4eca0fb 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c | |||
@@ -75,18 +75,53 @@ struct h5 { | |||
75 | u8 tx_seq; /* Next seq number to send */ | 75 | u8 tx_seq; /* Next seq number to send */ |
76 | u8 tx_ack; /* Next ack number to send */ | 76 | u8 tx_ack; /* Next ack number to send */ |
77 | 77 | ||
78 | enum { | ||
79 | H5_UNINITIALIZED, | ||
80 | H5_INITIALIZED, | ||
81 | H5_ACTIVE, | ||
82 | } state; | ||
83 | |||
78 | bool sleeping; | 84 | bool sleeping; |
79 | }; | 85 | }; |
80 | 86 | ||
81 | static void h5_reset_rx(struct h5 *h5); | 87 | static void h5_reset_rx(struct h5 *h5); |
82 | 88 | ||
89 | static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) | ||
90 | { | ||
91 | struct h5 *h5 = hu->priv; | ||
92 | struct sk_buff *nskb; | ||
93 | |||
94 | nskb = alloc_skb(3, GFP_ATOMIC); | ||
95 | if (!nskb) | ||
96 | return; | ||
97 | |||
98 | bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT; | ||
99 | |||
100 | memcpy(skb_put(nskb, len), data, len); | ||
101 | |||
102 | skb_queue_tail(&h5->unrel, nskb); | ||
103 | } | ||
104 | |||
83 | static void h5_timed_event(unsigned long arg) | 105 | static void h5_timed_event(unsigned long arg) |
84 | { | 106 | { |
107 | const unsigned char sync_req[] = { 0x01, 0x7e }; | ||
108 | const unsigned char conf_req[] = { 0x03, 0xfc, 0x01 }; | ||
85 | struct hci_uart *hu = (struct hci_uart *) arg; | 109 | struct hci_uart *hu = (struct hci_uart *) arg; |
86 | struct h5 *h5 = hu->priv; | 110 | struct h5 *h5 = hu->priv; |
87 | struct sk_buff *skb; | 111 | struct sk_buff *skb; |
88 | unsigned long flags; | 112 | unsigned long flags; |
89 | 113 | ||
114 | if (h5->state == H5_UNINITIALIZED) | ||
115 | h5_link_control(hu, sync_req, sizeof(sync_req)); | ||
116 | |||
117 | if (h5->state == H5_INITIALIZED) | ||
118 | h5_link_control(hu, conf_req, sizeof(conf_req)); | ||
119 | |||
120 | if (h5->state != H5_ACTIVE) { | ||
121 | mod_timer(&h5->timer, jiffies + H5_SYNC_TIMEOUT); | ||
122 | goto wakeup; | ||
123 | } | ||
124 | |||
90 | BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen); | 125 | BT_DBG("hu %p retransmitting %u pkts", hu, h5->unack.qlen); |
91 | 126 | ||
92 | spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); | 127 | spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); |
@@ -98,25 +133,10 @@ static void h5_timed_event(unsigned long arg) | |||
98 | 133 | ||
99 | spin_unlock_irqrestore(&h5->unack.lock, flags); | 134 | spin_unlock_irqrestore(&h5->unack.lock, flags); |
100 | 135 | ||
136 | wakeup: | ||
101 | hci_uart_tx_wakeup(hu); | 137 | hci_uart_tx_wakeup(hu); |
102 | } | 138 | } |
103 | 139 | ||
104 | static void h5_link_control(struct hci_uart *hu, const void *data, size_t len) | ||
105 | { | ||
106 | struct h5 *h5 = hu->priv; | ||
107 | struct sk_buff *nskb; | ||
108 | |||
109 | nskb = alloc_skb(3, GFP_ATOMIC); | ||
110 | if (!nskb) | ||
111 | return; | ||
112 | |||
113 | bt_cb(nskb)->pkt_type = HCI_3WIRE_LINK_PKT; | ||
114 | |||
115 | memcpy(skb_put(nskb, len), data, len); | ||
116 | |||
117 | skb_queue_tail(&h5->unrel, nskb); | ||
118 | } | ||
119 | |||
120 | static int h5_open(struct hci_uart *hu) | 140 | static int h5_open(struct hci_uart *hu) |
121 | { | 141 | { |
122 | struct h5 *h5; | 142 | struct h5 *h5; |
@@ -230,12 +250,14 @@ static void h5_handle_internal_rx(struct hci_uart *hu) | |||
230 | if (memcmp(data, sync_req, 2) == 0) { | 250 | if (memcmp(data, sync_req, 2) == 0) { |
231 | h5_link_control(hu, sync_rsp, 2); | 251 | h5_link_control(hu, sync_rsp, 2); |
232 | } else if (memcmp(data, sync_rsp, 2) == 0) { | 252 | } else if (memcmp(data, sync_rsp, 2) == 0) { |
253 | h5->state = H5_INITIALIZED; | ||
233 | h5_link_control(hu, conf_req, 3); | 254 | h5_link_control(hu, conf_req, 3); |
234 | } else if (memcmp(data, conf_req, 2) == 0) { | 255 | } else if (memcmp(data, conf_req, 2) == 0) { |
235 | h5_link_control(hu, conf_rsp, 2); | 256 | h5_link_control(hu, conf_rsp, 2); |
236 | h5_link_control(hu, conf_req, 3); | 257 | h5_link_control(hu, conf_req, 3); |
237 | } else if (memcmp(data, conf_rsp, 2) == 0) { | 258 | } else if (memcmp(data, conf_rsp, 2) == 0) { |
238 | BT_DBG("Three-wire init sequence complete"); | 259 | BT_DBG("Three-wire init sequence complete"); |
260 | h5->state = H5_ACTIVE; | ||
239 | hci_uart_init_ready(hu); | 261 | hci_uart_init_ready(hu); |
240 | return; | 262 | return; |
241 | } else if (memcmp(data, sleep_req, 2) == 0) { | 263 | } else if (memcmp(data, sleep_req, 2) == 0) { |
@@ -340,6 +362,12 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) | |||
340 | return 0; | 362 | return 0; |
341 | } | 363 | } |
342 | 364 | ||
365 | if (h5->state != H5_ACTIVE && | ||
366 | H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) { | ||
367 | BT_ERR("Non-link packet received in non-active state"); | ||
368 | h5_reset_rx(h5); | ||
369 | } | ||
370 | |||
343 | h5->rx_func = h5_rx_payload; | 371 | h5->rx_func = h5_rx_payload; |
344 | h5->rx_pending = H5_HDR_LEN(hdr); | 372 | h5->rx_pending = H5_HDR_LEN(hdr); |
345 | 373 | ||
@@ -468,6 +496,12 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb) | |||
468 | return 0; | 496 | return 0; |
469 | } | 497 | } |
470 | 498 | ||
499 | if (h5->state != H5_ACTIVE) { | ||
500 | BT_ERR("Ignoring HCI data in non-active state"); | ||
501 | kfree_skb(skb); | ||
502 | return 0; | ||
503 | } | ||
504 | |||
471 | switch (bt_cb(skb)->pkt_type) { | 505 | switch (bt_cb(skb)->pkt_type) { |
472 | case HCI_ACLDATA_PKT: | 506 | case HCI_ACLDATA_PKT: |
473 | case HCI_COMMAND_PKT: | 507 | case HCI_COMMAND_PKT: |