diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2012-07-16 09:12:08 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.co.uk> | 2012-07-17 13:48:24 -0400 |
commit | 43eb12d7896063d06011baab7097944a70c7e45a (patch) | |
tree | 36f01a58e8621517fa2051a577547ca33cdfb518 | |
parent | 01977c0bbbc834e57436be0bab31c3df11d61710 (diff) |
Bluetooth: Fix/implement Three-wire reliable packet sending
This patch should complete the necessary code for sending reliable
Three-wire packets.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Gustavo Padovan <gustavo.padovan@collabora.co.uk>
-rw-r--r-- | drivers/bluetooth/hci_h5.c | 87 |
1 files changed, 70 insertions, 17 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index f50afb2fb36..6c7b27e921a 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c | |||
@@ -64,15 +64,15 @@ struct h5 { | |||
64 | struct sk_buff *rx_skb; /* Receive buffer */ | 64 | struct sk_buff *rx_skb; /* Receive buffer */ |
65 | size_t rx_pending; /* Expecting more bytes */ | 65 | size_t rx_pending; /* Expecting more bytes */ |
66 | bool rx_esc; /* SLIP escape mode */ | 66 | bool rx_esc; /* SLIP escape mode */ |
67 | u8 rx_ack; /* Last ack number received */ | ||
68 | u8 rx_seq; /* Last seq number received */ | ||
67 | 69 | ||
68 | int (*rx_func) (struct hci_uart *hu, u8 c); | 70 | int (*rx_func) (struct hci_uart *hu, u8 c); |
69 | 71 | ||
70 | struct timer_list timer; /* Retransmission timer */ | 72 | struct timer_list timer; /* Retransmission timer */ |
71 | 73 | ||
72 | bool txack_req; | 74 | bool tx_ack_req; /* Pending ack to send */ |
73 | 75 | u8 tx_seq; /* Next seq number to send */ | |
74 | u8 next_ack; | ||
75 | u8 next_seq; | ||
76 | }; | 76 | }; |
77 | 77 | ||
78 | static void h5_reset_rx(struct h5 *h5); | 78 | static void h5_reset_rx(struct h5 *h5); |
@@ -89,7 +89,7 @@ static void h5_timed_event(unsigned long arg) | |||
89 | spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); | 89 | spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); |
90 | 90 | ||
91 | while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) { | 91 | while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) { |
92 | h5->next_seq = (h5->next_seq - 1) & 0x07; | 92 | h5->tx_seq = (h5->tx_seq - 1) & 0x07; |
93 | skb_queue_head(&h5->rel, skb); | 93 | skb_queue_head(&h5->rel, skb); |
94 | } | 94 | } |
95 | 95 | ||
@@ -138,6 +138,45 @@ static int h5_close(struct hci_uart *hu) | |||
138 | return 0; | 138 | return 0; |
139 | } | 139 | } |
140 | 140 | ||
141 | static void h5_pkt_cull(struct h5 *h5) | ||
142 | { | ||
143 | struct sk_buff *skb, *tmp; | ||
144 | unsigned long flags; | ||
145 | int i, to_remove; | ||
146 | u8 seq; | ||
147 | |||
148 | spin_lock_irqsave(&h5->unack.lock, flags); | ||
149 | |||
150 | to_remove = skb_queue_len(&h5->unack); | ||
151 | |||
152 | seq = h5->tx_seq; | ||
153 | |||
154 | while (to_remove > 0) { | ||
155 | if (h5->rx_ack == seq) | ||
156 | break; | ||
157 | |||
158 | to_remove--; | ||
159 | seq = (seq - 1) % 8; | ||
160 | } | ||
161 | |||
162 | if (seq != h5->rx_ack) | ||
163 | BT_ERR("Controller acked invalid packet"); | ||
164 | |||
165 | i = 0; | ||
166 | skb_queue_walk_safe(&h5->unack, skb, tmp) { | ||
167 | if (i++ >= to_remove) | ||
168 | break; | ||
169 | |||
170 | __skb_unlink(skb, &h5->unack); | ||
171 | kfree_skb(skb); | ||
172 | } | ||
173 | |||
174 | if (skb_queue_empty(&h5->unack)) | ||
175 | del_timer(&h5->timer); | ||
176 | |||
177 | spin_unlock_irqrestore(&h5->unack.lock, flags); | ||
178 | } | ||
179 | |||
141 | static void h5_handle_internal_rx(struct hci_uart *hu) | 180 | static void h5_handle_internal_rx(struct hci_uart *hu) |
142 | { | 181 | { |
143 | BT_DBG("%s", hu->hdev->name); | 182 | BT_DBG("%s", hu->hdev->name); |
@@ -146,17 +185,24 @@ static void h5_handle_internal_rx(struct hci_uart *hu) | |||
146 | static void h5_complete_rx_pkt(struct hci_uart *hu) | 185 | static void h5_complete_rx_pkt(struct hci_uart *hu) |
147 | { | 186 | { |
148 | struct h5 *h5 = hu->priv; | 187 | struct h5 *h5 = hu->priv; |
149 | u8 pkt_type; | 188 | const unsigned char *hdr = h5->rx_skb->data; |
150 | 189 | ||
151 | BT_DBG("%s", hu->hdev->name); | 190 | BT_DBG("%s", hu->hdev->name); |
152 | 191 | ||
153 | pkt_type = h5->rx_skb->data[1] & 0x0f; | 192 | if (H5_HDR_RELIABLE(hdr)) { |
193 | h5->tx_seq = (h5->tx_seq + 1) % 8; | ||
194 | h5->tx_ack_req = true; | ||
195 | } | ||
154 | 196 | ||
155 | switch (pkt_type) { | 197 | h5->rx_ack = H5_HDR_ACK(hdr); |
198 | |||
199 | h5_pkt_cull(h5); | ||
200 | |||
201 | switch (H5_HDR_PKT_TYPE(hdr)) { | ||
156 | case HCI_EVENT_PKT: | 202 | case HCI_EVENT_PKT: |
157 | case HCI_ACLDATA_PKT: | 203 | case HCI_ACLDATA_PKT: |
158 | case HCI_SCODATA_PKT: | 204 | case HCI_SCODATA_PKT: |
159 | bt_cb(h5->rx_skb)->pkt_type = pkt_type; | 205 | bt_cb(h5->rx_skb)->pkt_type = H5_HDR_PKT_TYPE(hdr); |
160 | 206 | ||
161 | /* Remove Three-wire header */ | 207 | /* Remove Three-wire header */ |
162 | skb_pull(h5->rx_skb, 4); | 208 | skb_pull(h5->rx_skb, 4); |
@@ -193,7 +239,7 @@ static int h5_rx_payload(struct hci_uart *hu, unsigned char c) | |||
193 | 239 | ||
194 | BT_DBG("%s 0x%02hhx", hu->hdev->name, c); | 240 | BT_DBG("%s 0x%02hhx", hu->hdev->name, c); |
195 | 241 | ||
196 | if ((hdr[0] >> 4) & 0x01) { | 242 | if (H5_HDR_CRC(hdr)) { |
197 | h5->rx_func = h5_rx_crc; | 243 | h5->rx_func = h5_rx_crc; |
198 | h5->rx_pending = 2; | 244 | h5->rx_pending = 2; |
199 | } else { | 245 | } else { |
@@ -217,8 +263,15 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) | |||
217 | return 0; | 263 | return 0; |
218 | } | 264 | } |
219 | 265 | ||
266 | if (H5_HDR_RELIABLE(hdr) && H5_HDR_SEQ(hdr) != h5->tx_seq) { | ||
267 | BT_ERR("Out-of-order packet arrived (%u != %u)", | ||
268 | H5_HDR_SEQ(hdr), h5->tx_seq); | ||
269 | h5_reset_rx(h5); | ||
270 | return 0; | ||
271 | } | ||
272 | |||
220 | h5->rx_func = h5_rx_payload; | 273 | h5->rx_func = h5_rx_payload; |
221 | h5->rx_pending = ((hdr[1] >> 4) & 0xff) + (hdr[2] << 4); | 274 | h5->rx_pending = H5_HDR_LEN(hdr); |
222 | 275 | ||
223 | return 0; | 276 | return 0; |
224 | } | 277 | } |
@@ -412,13 +465,13 @@ static struct sk_buff *h5_build_pkt(struct h5 *h5, bool rel, u8 pkt_type, | |||
412 | 465 | ||
413 | h5_slip_delim(nskb); | 466 | h5_slip_delim(nskb); |
414 | 467 | ||
415 | hdr[0] = h5->next_ack << 3; | 468 | hdr[0] = h5->rx_seq << 3; |
416 | h5->txack_req = false; | 469 | h5->tx_ack_req = false; |
417 | 470 | ||
418 | if (rel) { | 471 | if (rel) { |
419 | hdr[0] |= 1 << 7; | 472 | hdr[0] |= 1 << 7; |
420 | hdr[0] |= h5->next_seq; | 473 | hdr[0] |= h5->tx_seq; |
421 | h5->next_seq = (h5->next_seq + 1) % 8; | 474 | h5->tx_seq = (h5->tx_seq + 1) % 8; |
422 | } | 475 | } |
423 | 476 | ||
424 | hdr[1] = pkt_type | ((len & 0x0f) << 4); | 477 | hdr[1] = pkt_type | ((len & 0x0f) << 4); |
@@ -461,7 +514,7 @@ static struct sk_buff *h5_prepare_pkt(struct h5 *h5, u8 pkt_type, | |||
461 | 514 | ||
462 | static struct sk_buff *h5_prepare_ack(struct h5 *h5) | 515 | static struct sk_buff *h5_prepare_ack(struct h5 *h5) |
463 | { | 516 | { |
464 | h5->txack_req = false; | 517 | h5->tx_ack_req = false; |
465 | return NULL; | 518 | return NULL; |
466 | } | 519 | } |
467 | 520 | ||
@@ -505,7 +558,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu) | |||
505 | unlock: | 558 | unlock: |
506 | spin_unlock_irqrestore(&h5->unack.lock, flags); | 559 | spin_unlock_irqrestore(&h5->unack.lock, flags); |
507 | 560 | ||
508 | if (h5->txack_req) | 561 | if (h5->tx_ack_req) |
509 | return h5_prepare_ack(h5); | 562 | return h5_prepare_ack(h5); |
510 | 563 | ||
511 | return NULL; | 564 | return NULL; |