aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJohan Hedberg <johan.hedberg@intel.com>2012-07-16 09:12:06 -0400
committerGustavo Padovan <gustavo.padovan@collabora.co.uk>2012-07-17 13:48:21 -0400
commitc0a1b73c3f7d0cf3a93b58507b411911659d1b6a (patch)
tree0a914f837ca27e51bad8fca4194e079a465ac1c4
parentbc1f35b9113275433021592954f54576ba587899 (diff)
Bluetooth: Add initial packet sending support to Three-wire UART
This patch adds initial packed encoding and sending support to the Three-wire UART HCI transport driver. 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.c105
1 files changed, 98 insertions, 7 deletions
diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c
index ddf7ebbaaef..6df4c07bbd7 100644
--- a/drivers/bluetooth/hci_h5.c
+++ b/drivers/bluetooth/hci_h5.c
@@ -30,6 +30,9 @@
30 30
31#include "hci_uart.h" 31#include "hci_uart.h"
32 32
33#define HCI_3WIRE_ACK_PKT 0
34#define HCI_3WIRE_LINK_PKT 15
35
33#define H5_TXWINSIZE 4 36#define H5_TXWINSIZE 4
34 37
35#define H5_ACK_TIMEOUT msecs_to_jiffies(250) 38#define H5_ACK_TIMEOUT msecs_to_jiffies(250)
@@ -60,7 +63,8 @@ struct h5 {
60 63
61 bool txack_req; 64 bool txack_req;
62 65
63 u8 msgq_txseq; 66 u8 next_ack;
67 u8 next_seq;
64}; 68};
65 69
66static void h5_reset_rx(struct h5 *h5); 70static void h5_reset_rx(struct h5 *h5);
@@ -77,7 +81,7 @@ static void h5_timed_event(unsigned long arg)
77 spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING); 81 spin_lock_irqsave_nested(&h5->unack.lock, flags, SINGLE_DEPTH_NESTING);
78 82
79 while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) { 83 while ((skb = __skb_dequeue_tail(&h5->unack)) != NULL) {
80 h5->msgq_txseq = (h5->msgq_txseq - 1) & 0x07; 84 h5->next_seq = (h5->next_seq - 1) & 0x07;
81 skb_queue_head(&h5->rel, skb); 85 skb_queue_head(&h5->rel, skb);
82 } 86 }
83 87
@@ -355,10 +359,96 @@ static int h5_enqueue(struct hci_uart *hu, struct sk_buff *skb)
355 return 0; 359 return 0;
356} 360}
357 361
358static struct sk_buff *h5_prepare_pkt(struct h5 *h5, struct sk_buff *skb) 362static void h5_slip_delim(struct sk_buff *skb)
363{
364 const char delim = SLIP_DELIMITER;
365
366 memcpy(skb_put(skb, 1), &delim, 1);
367}
368
369static void h5_slip_one_byte(struct sk_buff *skb, u8 c)
370{
371 const char esc_delim[2] = { SLIP_ESC, SLIP_ESC_DELIM };
372 const char esc_esc[2] = { SLIP_ESC, SLIP_ESC_ESC };
373
374 switch (c) {
375 case SLIP_DELIMITER:
376 memcpy(skb_put(skb, 2), &esc_delim, 2);
377 break;
378 case SLIP_ESC:
379 memcpy(skb_put(skb, 2), &esc_esc, 2);
380 break;
381 default:
382 memcpy(skb_put(skb, 1), &c, 1);
383 }
384}
385
386static struct sk_buff *h5_build_pkt(struct h5 *h5, bool rel, u8 pkt_type,
387 const u8 *data, size_t len)
359{ 388{
389 struct sk_buff *nskb;
390 u8 hdr[4];
391 int i;
392
393 /*
394 * Max len of packet: (original len + 4 (H5 hdr) + 2 (crc)) * 2
395 * (because bytes 0xc0 and 0xdb are escaped, worst case is when
396 * the packet is all made of 0xc0 and 0xdb) + 2 (0xc0
397 * delimiters at start and end).
398 */
399 nskb = alloc_skb((len + 6) * 2 + 2, GFP_ATOMIC);
400 if (!nskb)
401 return NULL;
402
403 bt_cb(nskb)->pkt_type = pkt_type;
404
405 h5_slip_delim(nskb);
406
407 hdr[0] = h5->next_ack << 3;
360 h5->txack_req = false; 408 h5->txack_req = false;
361 return NULL; 409
410 if (rel) {
411 hdr[0] |= 1 << 7;
412 hdr[0] |= h5->next_seq;
413 h5->next_seq = (h5->next_seq + 1) % 8;
414 }
415
416 hdr[1] = pkt_type | ((len & 0x0f) << 4);
417 hdr[2] = len >> 4;
418 hdr[3] = ~((hdr[0] + hdr[1] + hdr[2]) & 0xff);
419
420 for (i = 0; i < 4; i++)
421 h5_slip_one_byte(nskb, hdr[i]);
422
423 for (i = 0; i < len; i++)
424 h5_slip_one_byte(nskb, data[i]);
425
426 h5_slip_delim(nskb);
427
428 return nskb;
429}
430
431static struct sk_buff *h5_prepare_pkt(struct h5 *h5, u8 pkt_type,
432 const u8 *data, size_t len)
433{
434 bool rel;
435
436 switch (pkt_type) {
437 case HCI_ACLDATA_PKT:
438 case HCI_COMMAND_PKT:
439 rel = true;
440 break;
441 case HCI_SCODATA_PKT:
442 case HCI_3WIRE_LINK_PKT:
443 case HCI_3WIRE_ACK_PKT:
444 rel = false;
445 break;
446 default:
447 BT_ERR("Unknown packet type %u", pkt_type);
448 return NULL;
449 }
450
451 return h5_build_pkt(h5, rel, pkt_type, data, len);
362} 452}
363 453
364static struct sk_buff *h5_prepare_ack(struct h5 *h5) 454static struct sk_buff *h5_prepare_ack(struct h5 *h5)
@@ -374,7 +464,8 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
374 struct sk_buff *skb, *nskb; 464 struct sk_buff *skb, *nskb;
375 465
376 if ((skb = skb_dequeue(&h5->unrel)) != NULL) { 466 if ((skb = skb_dequeue(&h5->unrel)) != NULL) {
377 nskb = h5_prepare_pkt(h5, skb); 467 nskb = h5_prepare_pkt(h5, bt_cb(skb)->pkt_type,
468 skb->data, skb->len);
378 if (nskb) { 469 if (nskb) {
379 kfree_skb(skb); 470 kfree_skb(skb);
380 return nskb; 471 return nskb;
@@ -390,8 +481,8 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
390 goto unlock; 481 goto unlock;
391 482
392 if ((skb = skb_dequeue(&h5->rel)) != NULL) { 483 if ((skb = skb_dequeue(&h5->rel)) != NULL) {
393 nskb = h5_prepare_pkt(h5, skb); 484 nskb = h5_prepare_pkt(h5, bt_cb(skb)->pkt_type,
394 485 skb->data, skb->len);
395 if (nskb) { 486 if (nskb) {
396 __skb_queue_tail(&h5->unack, skb); 487 __skb_queue_tail(&h5->unack, skb);
397 mod_timer(&h5->timer, jiffies + H5_ACK_TIMEOUT); 488 mod_timer(&h5->timer, jiffies + H5_ACK_TIMEOUT);