xref: /openbmc/linux/drivers/bluetooth/h4_recv.h (revision 75bf465f0bc33e9b776a46d6a1b9b990f5fb7c37)
1*1a59d1b8SThomas Gleixner /* SPDX-License-Identifier: GPL-2.0-or-later */
207eb96a5SMarcel Holtmann /*
307eb96a5SMarcel Holtmann  *
407eb96a5SMarcel Holtmann  *  Generic Bluetooth HCI UART driver
507eb96a5SMarcel Holtmann  *
607eb96a5SMarcel Holtmann  *  Copyright (C) 2015-2018  Intel Corporation
707eb96a5SMarcel Holtmann  */
807eb96a5SMarcel Holtmann 
907eb96a5SMarcel Holtmann #include <asm/unaligned.h>
1007eb96a5SMarcel Holtmann 
1107eb96a5SMarcel Holtmann struct h4_recv_pkt {
1207eb96a5SMarcel Holtmann 	u8  type;	/* Packet type */
1307eb96a5SMarcel Holtmann 	u8  hlen;	/* Header length */
1407eb96a5SMarcel Holtmann 	u8  loff;	/* Data length offset in header */
1507eb96a5SMarcel Holtmann 	u8  lsize;	/* Data length field size */
1607eb96a5SMarcel Holtmann 	u16 maxlen;	/* Max overall packet length */
1707eb96a5SMarcel Holtmann 	int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
1807eb96a5SMarcel Holtmann };
1907eb96a5SMarcel Holtmann 
2007eb96a5SMarcel Holtmann #define H4_RECV_ACL \
2107eb96a5SMarcel Holtmann 	.type = HCI_ACLDATA_PKT, \
2207eb96a5SMarcel Holtmann 	.hlen = HCI_ACL_HDR_SIZE, \
2307eb96a5SMarcel Holtmann 	.loff = 2, \
2407eb96a5SMarcel Holtmann 	.lsize = 2, \
2507eb96a5SMarcel Holtmann 	.maxlen = HCI_MAX_FRAME_SIZE \
2607eb96a5SMarcel Holtmann 
2707eb96a5SMarcel Holtmann #define H4_RECV_SCO \
2807eb96a5SMarcel Holtmann 	.type = HCI_SCODATA_PKT, \
2907eb96a5SMarcel Holtmann 	.hlen = HCI_SCO_HDR_SIZE, \
3007eb96a5SMarcel Holtmann 	.loff = 2, \
3107eb96a5SMarcel Holtmann 	.lsize = 1, \
3207eb96a5SMarcel Holtmann 	.maxlen = HCI_MAX_SCO_SIZE
3307eb96a5SMarcel Holtmann 
3407eb96a5SMarcel Holtmann #define H4_RECV_EVENT \
3507eb96a5SMarcel Holtmann 	.type = HCI_EVENT_PKT, \
3607eb96a5SMarcel Holtmann 	.hlen = HCI_EVENT_HDR_SIZE, \
3707eb96a5SMarcel Holtmann 	.loff = 1, \
3807eb96a5SMarcel Holtmann 	.lsize = 1, \
3907eb96a5SMarcel Holtmann 	.maxlen = HCI_MAX_EVENT_SIZE
4007eb96a5SMarcel Holtmann 
h4_recv_buf(struct hci_dev * hdev,struct sk_buff * skb,const unsigned char * buffer,int count,const struct h4_recv_pkt * pkts,int pkts_count)4107eb96a5SMarcel Holtmann static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
4207eb96a5SMarcel Holtmann 					  struct sk_buff *skb,
4307eb96a5SMarcel Holtmann 					  const unsigned char *buffer,
4407eb96a5SMarcel Holtmann 					  int count,
4507eb96a5SMarcel Holtmann 					  const struct h4_recv_pkt *pkts,
4607eb96a5SMarcel Holtmann 					  int pkts_count)
4707eb96a5SMarcel Holtmann {
481dc2d785SMyungho Jung 	/* Check for error from previous call */
491dc2d785SMyungho Jung 	if (IS_ERR(skb))
501dc2d785SMyungho Jung 		skb = NULL;
511dc2d785SMyungho Jung 
5207eb96a5SMarcel Holtmann 	while (count) {
5307eb96a5SMarcel Holtmann 		int i, len;
5407eb96a5SMarcel Holtmann 
5507eb96a5SMarcel Holtmann 		if (!skb) {
5607eb96a5SMarcel Holtmann 			for (i = 0; i < pkts_count; i++) {
5707eb96a5SMarcel Holtmann 				if (buffer[0] != (&pkts[i])->type)
5807eb96a5SMarcel Holtmann 					continue;
5907eb96a5SMarcel Holtmann 
6007eb96a5SMarcel Holtmann 				skb = bt_skb_alloc((&pkts[i])->maxlen,
6107eb96a5SMarcel Holtmann 						   GFP_ATOMIC);
6207eb96a5SMarcel Holtmann 				if (!skb)
6307eb96a5SMarcel Holtmann 					return ERR_PTR(-ENOMEM);
6407eb96a5SMarcel Holtmann 
6507eb96a5SMarcel Holtmann 				hci_skb_pkt_type(skb) = (&pkts[i])->type;
6607eb96a5SMarcel Holtmann 				hci_skb_expect(skb) = (&pkts[i])->hlen;
6707eb96a5SMarcel Holtmann 				break;
6807eb96a5SMarcel Holtmann 			}
6907eb96a5SMarcel Holtmann 
7007eb96a5SMarcel Holtmann 			/* Check for invalid packet type */
7107eb96a5SMarcel Holtmann 			if (!skb)
7207eb96a5SMarcel Holtmann 				return ERR_PTR(-EILSEQ);
7307eb96a5SMarcel Holtmann 
7407eb96a5SMarcel Holtmann 			count -= 1;
7507eb96a5SMarcel Holtmann 			buffer += 1;
7607eb96a5SMarcel Holtmann 		}
7707eb96a5SMarcel Holtmann 
7807eb96a5SMarcel Holtmann 		len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
7907eb96a5SMarcel Holtmann 		skb_put_data(skb, buffer, len);
8007eb96a5SMarcel Holtmann 
8107eb96a5SMarcel Holtmann 		count -= len;
8207eb96a5SMarcel Holtmann 		buffer += len;
8307eb96a5SMarcel Holtmann 
8407eb96a5SMarcel Holtmann 		/* Check for partial packet */
8507eb96a5SMarcel Holtmann 		if (skb->len < hci_skb_expect(skb))
8607eb96a5SMarcel Holtmann 			continue;
8707eb96a5SMarcel Holtmann 
8807eb96a5SMarcel Holtmann 		for (i = 0; i < pkts_count; i++) {
8907eb96a5SMarcel Holtmann 			if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
9007eb96a5SMarcel Holtmann 				break;
9107eb96a5SMarcel Holtmann 		}
9207eb96a5SMarcel Holtmann 
9307eb96a5SMarcel Holtmann 		if (i >= pkts_count) {
9407eb96a5SMarcel Holtmann 			kfree_skb(skb);
9507eb96a5SMarcel Holtmann 			return ERR_PTR(-EILSEQ);
9607eb96a5SMarcel Holtmann 		}
9707eb96a5SMarcel Holtmann 
9807eb96a5SMarcel Holtmann 		if (skb->len == (&pkts[i])->hlen) {
9907eb96a5SMarcel Holtmann 			u16 dlen;
10007eb96a5SMarcel Holtmann 
10107eb96a5SMarcel Holtmann 			switch ((&pkts[i])->lsize) {
10207eb96a5SMarcel Holtmann 			case 0:
10307eb96a5SMarcel Holtmann 				/* No variable data length */
10407eb96a5SMarcel Holtmann 				dlen = 0;
10507eb96a5SMarcel Holtmann 				break;
10607eb96a5SMarcel Holtmann 			case 1:
10707eb96a5SMarcel Holtmann 				/* Single octet variable length */
10807eb96a5SMarcel Holtmann 				dlen = skb->data[(&pkts[i])->loff];
10907eb96a5SMarcel Holtmann 				hci_skb_expect(skb) += dlen;
11007eb96a5SMarcel Holtmann 
11107eb96a5SMarcel Holtmann 				if (skb_tailroom(skb) < dlen) {
11207eb96a5SMarcel Holtmann 					kfree_skb(skb);
11307eb96a5SMarcel Holtmann 					return ERR_PTR(-EMSGSIZE);
11407eb96a5SMarcel Holtmann 				}
11507eb96a5SMarcel Holtmann 				break;
11607eb96a5SMarcel Holtmann 			case 2:
11707eb96a5SMarcel Holtmann 				/* Double octet variable length */
11807eb96a5SMarcel Holtmann 				dlen = get_unaligned_le16(skb->data +
11907eb96a5SMarcel Holtmann 							  (&pkts[i])->loff);
12007eb96a5SMarcel Holtmann 				hci_skb_expect(skb) += dlen;
12107eb96a5SMarcel Holtmann 
12207eb96a5SMarcel Holtmann 				if (skb_tailroom(skb) < dlen) {
12307eb96a5SMarcel Holtmann 					kfree_skb(skb);
12407eb96a5SMarcel Holtmann 					return ERR_PTR(-EMSGSIZE);
12507eb96a5SMarcel Holtmann 				}
12607eb96a5SMarcel Holtmann 				break;
12707eb96a5SMarcel Holtmann 			default:
12807eb96a5SMarcel Holtmann 				/* Unsupported variable length */
12907eb96a5SMarcel Holtmann 				kfree_skb(skb);
13007eb96a5SMarcel Holtmann 				return ERR_PTR(-EILSEQ);
13107eb96a5SMarcel Holtmann 			}
13207eb96a5SMarcel Holtmann 
13307eb96a5SMarcel Holtmann 			if (!dlen) {
13407eb96a5SMarcel Holtmann 				/* No more data, complete frame */
13507eb96a5SMarcel Holtmann 				(&pkts[i])->recv(hdev, skb);
13607eb96a5SMarcel Holtmann 				skb = NULL;
13707eb96a5SMarcel Holtmann 			}
13807eb96a5SMarcel Holtmann 		} else {
13907eb96a5SMarcel Holtmann 			/* Complete frame */
14007eb96a5SMarcel Holtmann 			(&pkts[i])->recv(hdev, skb);
14107eb96a5SMarcel Holtmann 			skb = NULL;
14207eb96a5SMarcel Holtmann 		}
14307eb96a5SMarcel Holtmann 	}
14407eb96a5SMarcel Holtmann 
14507eb96a5SMarcel Holtmann 	return skb;
14607eb96a5SMarcel Holtmann }
147