Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1a38d70 authored by Marcel Holtmann's avatar Marcel Holtmann
Browse files

Bluetooth: hci_uart: Introduce h4_recv_buf helper function



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: default avatarMarcel Holtmann <marcel@holtmann.org>
Signed-off-by: default avatarJohan Hedberg <johan.hedberg@intel.com>
parent 9d1c40eb
Loading
Loading
Loading
Loading
+98 −0
Original line number Diff line number Diff line
@@ -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;
}
+3 −0
Original line number Diff line number Diff line
@@ -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