Bluetooth: hci_uart: Introduce h4_recv_buf helper function
authorMarcel Holtmann <marcel@holtmann.org>
Sun, 5 Apr 2015 04:59:24 +0000 (21:59 -0700)
committerMarcel Holtmann <marcel@holtmann.org>
Tue, 7 Apr 2015 16:47:09 +0000 (18:47 +0200)
The h4_recv_buf helper function can be used for receiving H:4 packets
out of a TTY stream. It is self-contained and allows for reuse by all
HCI UART protocols.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
drivers/bluetooth/hci_h4.c
drivers/bluetooth/hci_uart.h

index 97a5df4941b4fa8fbabfcbadfdc3ee4fb93b7e74..09270bc2665495a2f3010198187f03c48115933f 100644 (file)
@@ -161,3 +161,101 @@ int __exit h4_deinit(void)
 {
        return hci_uart_unregister_proto(&h4p);
 }
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+                           const unsigned char *buffer, int count)
+{
+       while (count) {
+               int len;
+
+               if (!skb) {
+                       switch (buffer[0]) {
+                       case HCI_ACLDATA_PKT:
+                               skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE,
+                                                  GFP_ATOMIC);
+                               if (!skb)
+                                       return ERR_PTR(-ENOMEM);
+
+                               bt_cb(skb)->pkt_type = HCI_ACLDATA_PKT;
+                               bt_cb(skb)->expect = HCI_ACL_HDR_SIZE;
+                               break;
+                       case HCI_SCODATA_PKT:
+                               skb = bt_skb_alloc(HCI_MAX_SCO_SIZE,
+                                                  GFP_ATOMIC);
+                               if (!skb)
+                                       return ERR_PTR(-ENOMEM);
+
+                               bt_cb(skb)->pkt_type = HCI_SCODATA_PKT;
+                               bt_cb(skb)->expect = HCI_SCO_HDR_SIZE;
+                               break;
+                       case HCI_EVENT_PKT:
+                               skb = bt_skb_alloc(HCI_MAX_EVENT_SIZE,
+                                                  GFP_ATOMIC);
+                               if (!skb)
+                                       return ERR_PTR(-ENOMEM);
+
+                               bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
+                               bt_cb(skb)->expect = HCI_EVENT_HDR_SIZE;
+                               break;
+                       default:
+                               return ERR_PTR(-EILSEQ);
+                       }
+
+                       count -= 1;
+                       buffer += 1;
+               }
+
+               len = min_t(uint, bt_cb(skb)->expect, count);
+               memcpy(skb_put(skb, len), buffer, len);
+
+               count -= len;
+               buffer += len;
+               bt_cb(skb)->expect -= len;
+
+               switch (bt_cb(skb)->pkt_type) {
+               case HCI_ACLDATA_PKT:
+                       if (skb->len == HCI_ACL_HDR_SIZE) {
+                               __le16 dlen = hci_acl_hdr(skb)->dlen;
+
+                               /* Complete ACL header */
+                               bt_cb(skb)->expect = __le16_to_cpu(dlen);
+
+                               if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+                                       kfree_skb(skb);
+                                       return ERR_PTR(-EMSGSIZE);
+                               }
+                       }
+                       break;
+               case HCI_SCODATA_PKT:
+                       if (skb->len == HCI_SCO_HDR_SIZE) {
+                               /* Complete SCO header */
+                               bt_cb(skb)->expect = hci_sco_hdr(skb)->dlen;
+
+                               if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+                                       kfree_skb(skb);
+                                       return ERR_PTR(-EMSGSIZE);
+                               }
+                       }
+                       break;
+               case HCI_EVENT_PKT:
+                       if (skb->len == HCI_EVENT_HDR_SIZE) {
+                               /* Complete event header */
+                               bt_cb(skb)->expect = hci_event_hdr(skb)->plen;
+
+                               if (skb_tailroom(skb) < bt_cb(skb)->expect) {
+                                       kfree_skb(skb);
+                                       return ERR_PTR(-EMSGSIZE);
+                               }
+                       }
+                       break;
+               }
+
+               if (bt_cb(skb)->expect == 0) {
+                       /* Complete frame */
+                       hci_recv_frame(hdev, skb);
+                       skb = NULL;
+               }
+       }
+
+       return skb;
+}
index 79bbcefb01127df94ab7c780fcfb0077444f4842..53b329229f2e81899b7e07c501ec8abf7d5dc70c 100644 (file)
@@ -99,6 +99,9 @@ int hci_uart_init_ready(struct hci_uart *hu);
 #ifdef CONFIG_BT_HCIUART_H4
 int h4_init(void);
 int h4_deinit(void);
+
+struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
+                           const unsigned char *buffer, int count);
 #endif
 
 #ifdef CONFIG_BT_HCIUART_BCSP