diff options
author | Johan Hedberg <johan.hedberg@intel.com> | 2012-07-16 09:12:06 -0400 |
---|---|---|
committer | Gustavo Padovan <gustavo.padovan@collabora.co.uk> | 2012-07-17 13:48:21 -0400 |
commit | c0a1b73c3f7d0cf3a93b58507b411911659d1b6a (patch) | |
tree | 0a914f837ca27e51bad8fca4194e079a465ac1c4 | |
parent | bc1f35b9113275433021592954f54576ba587899 (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.c | 105 |
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 | ||
66 | static void h5_reset_rx(struct h5 *h5); | 70 | static 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 | ||
358 | static struct sk_buff *h5_prepare_pkt(struct h5 *h5, struct sk_buff *skb) | 362 | static 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 | |||
369 | static 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 | |||
386 | static 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 | |||
431 | static 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 | ||
364 | static struct sk_buff *h5_prepare_ack(struct h5 *h5) | 454 | static 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); |