xref: /openbmc/linux/drivers/bluetooth/h4_recv.h (revision 1dc2d785156cbdc80806c32e8d2c7c735d0b4721)
107eb96a5SMarcel Holtmann /*
207eb96a5SMarcel Holtmann  *
307eb96a5SMarcel Holtmann  *  Generic Bluetooth HCI UART driver
407eb96a5SMarcel Holtmann  *
507eb96a5SMarcel Holtmann  *  Copyright (C) 2015-2018  Intel Corporation
607eb96a5SMarcel Holtmann  *
707eb96a5SMarcel Holtmann  *
807eb96a5SMarcel Holtmann  *  This program is free software; you can redistribute it and/or modify
907eb96a5SMarcel Holtmann  *  it under the terms of the GNU General Public License as published by
1007eb96a5SMarcel Holtmann  *  the Free Software Foundation; either version 2 of the License, or
1107eb96a5SMarcel Holtmann  *  (at your option) any later version.
1207eb96a5SMarcel Holtmann  *
1307eb96a5SMarcel Holtmann  *  This program is distributed in the hope that it will be useful,
1407eb96a5SMarcel Holtmann  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1507eb96a5SMarcel Holtmann  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1607eb96a5SMarcel Holtmann  *  GNU General Public License for more details.
1707eb96a5SMarcel Holtmann  *
1807eb96a5SMarcel Holtmann  *  You should have received a copy of the GNU General Public License
1907eb96a5SMarcel Holtmann  *  along with this program; if not, write to the Free Software
2007eb96a5SMarcel Holtmann  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2107eb96a5SMarcel Holtmann  *
2207eb96a5SMarcel Holtmann  */
2307eb96a5SMarcel Holtmann 
2407eb96a5SMarcel Holtmann #include <asm/unaligned.h>
2507eb96a5SMarcel Holtmann 
2607eb96a5SMarcel Holtmann struct h4_recv_pkt {
2707eb96a5SMarcel Holtmann 	u8  type;	/* Packet type */
2807eb96a5SMarcel Holtmann 	u8  hlen;	/* Header length */
2907eb96a5SMarcel Holtmann 	u8  loff;	/* Data length offset in header */
3007eb96a5SMarcel Holtmann 	u8  lsize;	/* Data length field size */
3107eb96a5SMarcel Holtmann 	u16 maxlen;	/* Max overall packet length */
3207eb96a5SMarcel Holtmann 	int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
3307eb96a5SMarcel Holtmann };
3407eb96a5SMarcel Holtmann 
3507eb96a5SMarcel Holtmann #define H4_RECV_ACL \
3607eb96a5SMarcel Holtmann 	.type = HCI_ACLDATA_PKT, \
3707eb96a5SMarcel Holtmann 	.hlen = HCI_ACL_HDR_SIZE, \
3807eb96a5SMarcel Holtmann 	.loff = 2, \
3907eb96a5SMarcel Holtmann 	.lsize = 2, \
4007eb96a5SMarcel Holtmann 	.maxlen = HCI_MAX_FRAME_SIZE \
4107eb96a5SMarcel Holtmann 
4207eb96a5SMarcel Holtmann #define H4_RECV_SCO \
4307eb96a5SMarcel Holtmann 	.type = HCI_SCODATA_PKT, \
4407eb96a5SMarcel Holtmann 	.hlen = HCI_SCO_HDR_SIZE, \
4507eb96a5SMarcel Holtmann 	.loff = 2, \
4607eb96a5SMarcel Holtmann 	.lsize = 1, \
4707eb96a5SMarcel Holtmann 	.maxlen = HCI_MAX_SCO_SIZE
4807eb96a5SMarcel Holtmann 
4907eb96a5SMarcel Holtmann #define H4_RECV_EVENT \
5007eb96a5SMarcel Holtmann 	.type = HCI_EVENT_PKT, \
5107eb96a5SMarcel Holtmann 	.hlen = HCI_EVENT_HDR_SIZE, \
5207eb96a5SMarcel Holtmann 	.loff = 1, \
5307eb96a5SMarcel Holtmann 	.lsize = 1, \
5407eb96a5SMarcel Holtmann 	.maxlen = HCI_MAX_EVENT_SIZE
5507eb96a5SMarcel Holtmann 
5607eb96a5SMarcel Holtmann static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
5707eb96a5SMarcel Holtmann 					  struct sk_buff *skb,
5807eb96a5SMarcel Holtmann 					  const unsigned char *buffer,
5907eb96a5SMarcel Holtmann 					  int count,
6007eb96a5SMarcel Holtmann 					  const struct h4_recv_pkt *pkts,
6107eb96a5SMarcel Holtmann 					  int pkts_count)
6207eb96a5SMarcel Holtmann {
63*1dc2d785SMyungho Jung 	/* Check for error from previous call */
64*1dc2d785SMyungho Jung 	if (IS_ERR(skb))
65*1dc2d785SMyungho Jung 		skb = NULL;
66*1dc2d785SMyungho Jung 
6707eb96a5SMarcel Holtmann 	while (count) {
6807eb96a5SMarcel Holtmann 		int i, len;
6907eb96a5SMarcel Holtmann 
7007eb96a5SMarcel Holtmann 		if (!count)
7107eb96a5SMarcel Holtmann 			break;
7207eb96a5SMarcel Holtmann 
7307eb96a5SMarcel Holtmann 		if (!skb) {
7407eb96a5SMarcel Holtmann 			for (i = 0; i < pkts_count; i++) {
7507eb96a5SMarcel Holtmann 				if (buffer[0] != (&pkts[i])->type)
7607eb96a5SMarcel Holtmann 					continue;
7707eb96a5SMarcel Holtmann 
7807eb96a5SMarcel Holtmann 				skb = bt_skb_alloc((&pkts[i])->maxlen,
7907eb96a5SMarcel Holtmann 						   GFP_ATOMIC);
8007eb96a5SMarcel Holtmann 				if (!skb)
8107eb96a5SMarcel Holtmann 					return ERR_PTR(-ENOMEM);
8207eb96a5SMarcel Holtmann 
8307eb96a5SMarcel Holtmann 				hci_skb_pkt_type(skb) = (&pkts[i])->type;
8407eb96a5SMarcel Holtmann 				hci_skb_expect(skb) = (&pkts[i])->hlen;
8507eb96a5SMarcel Holtmann 				break;
8607eb96a5SMarcel Holtmann 			}
8707eb96a5SMarcel Holtmann 
8807eb96a5SMarcel Holtmann 			/* Check for invalid packet type */
8907eb96a5SMarcel Holtmann 			if (!skb)
9007eb96a5SMarcel Holtmann 				return ERR_PTR(-EILSEQ);
9107eb96a5SMarcel Holtmann 
9207eb96a5SMarcel Holtmann 			count -= 1;
9307eb96a5SMarcel Holtmann 			buffer += 1;
9407eb96a5SMarcel Holtmann 		}
9507eb96a5SMarcel Holtmann 
9607eb96a5SMarcel Holtmann 		len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
9707eb96a5SMarcel Holtmann 		skb_put_data(skb, buffer, len);
9807eb96a5SMarcel Holtmann 
9907eb96a5SMarcel Holtmann 		count -= len;
10007eb96a5SMarcel Holtmann 		buffer += len;
10107eb96a5SMarcel Holtmann 
10207eb96a5SMarcel Holtmann 		/* Check for partial packet */
10307eb96a5SMarcel Holtmann 		if (skb->len < hci_skb_expect(skb))
10407eb96a5SMarcel Holtmann 			continue;
10507eb96a5SMarcel Holtmann 
10607eb96a5SMarcel Holtmann 		for (i = 0; i < pkts_count; i++) {
10707eb96a5SMarcel Holtmann 			if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
10807eb96a5SMarcel Holtmann 				break;
10907eb96a5SMarcel Holtmann 		}
11007eb96a5SMarcel Holtmann 
11107eb96a5SMarcel Holtmann 		if (i >= pkts_count) {
11207eb96a5SMarcel Holtmann 			kfree_skb(skb);
11307eb96a5SMarcel Holtmann 			return ERR_PTR(-EILSEQ);
11407eb96a5SMarcel Holtmann 		}
11507eb96a5SMarcel Holtmann 
11607eb96a5SMarcel Holtmann 		if (skb->len == (&pkts[i])->hlen) {
11707eb96a5SMarcel Holtmann 			u16 dlen;
11807eb96a5SMarcel Holtmann 
11907eb96a5SMarcel Holtmann 			switch ((&pkts[i])->lsize) {
12007eb96a5SMarcel Holtmann 			case 0:
12107eb96a5SMarcel Holtmann 				/* No variable data length */
12207eb96a5SMarcel Holtmann 				dlen = 0;
12307eb96a5SMarcel Holtmann 				break;
12407eb96a5SMarcel Holtmann 			case 1:
12507eb96a5SMarcel Holtmann 				/* Single octet variable length */
12607eb96a5SMarcel Holtmann 				dlen = skb->data[(&pkts[i])->loff];
12707eb96a5SMarcel Holtmann 				hci_skb_expect(skb) += dlen;
12807eb96a5SMarcel Holtmann 
12907eb96a5SMarcel Holtmann 				if (skb_tailroom(skb) < dlen) {
13007eb96a5SMarcel Holtmann 					kfree_skb(skb);
13107eb96a5SMarcel Holtmann 					return ERR_PTR(-EMSGSIZE);
13207eb96a5SMarcel Holtmann 				}
13307eb96a5SMarcel Holtmann 				break;
13407eb96a5SMarcel Holtmann 			case 2:
13507eb96a5SMarcel Holtmann 				/* Double octet variable length */
13607eb96a5SMarcel Holtmann 				dlen = get_unaligned_le16(skb->data +
13707eb96a5SMarcel Holtmann 							  (&pkts[i])->loff);
13807eb96a5SMarcel Holtmann 				hci_skb_expect(skb) += dlen;
13907eb96a5SMarcel Holtmann 
14007eb96a5SMarcel Holtmann 				if (skb_tailroom(skb) < dlen) {
14107eb96a5SMarcel Holtmann 					kfree_skb(skb);
14207eb96a5SMarcel Holtmann 					return ERR_PTR(-EMSGSIZE);
14307eb96a5SMarcel Holtmann 				}
14407eb96a5SMarcel Holtmann 				break;
14507eb96a5SMarcel Holtmann 			default:
14607eb96a5SMarcel Holtmann 				/* Unsupported variable length */
14707eb96a5SMarcel Holtmann 				kfree_skb(skb);
14807eb96a5SMarcel Holtmann 				return ERR_PTR(-EILSEQ);
14907eb96a5SMarcel Holtmann 			}
15007eb96a5SMarcel Holtmann 
15107eb96a5SMarcel Holtmann 			if (!dlen) {
15207eb96a5SMarcel Holtmann 				/* No more data, complete frame */
15307eb96a5SMarcel Holtmann 				(&pkts[i])->recv(hdev, skb);
15407eb96a5SMarcel Holtmann 				skb = NULL;
15507eb96a5SMarcel Holtmann 			}
15607eb96a5SMarcel Holtmann 		} else {
15707eb96a5SMarcel Holtmann 			/* Complete frame */
15807eb96a5SMarcel Holtmann 			(&pkts[i])->recv(hdev, skb);
15907eb96a5SMarcel Holtmann 			skb = NULL;
16007eb96a5SMarcel Holtmann 		}
16107eb96a5SMarcel Holtmann 	}
16207eb96a5SMarcel Holtmann 
16307eb96a5SMarcel Holtmann 	return skb;
16407eb96a5SMarcel Holtmann }
165