xref: /openbmc/linux/drivers/bluetooth/h4_recv.h (revision 2359ccdd)
1 /*
2  *
3  *  Generic Bluetooth HCI UART driver
4  *
5  *  Copyright (C) 2015-2018  Intel Corporation
6  *
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  *
22  */
23 
24 #include <asm/unaligned.h>
25 
26 struct h4_recv_pkt {
27 	u8  type;	/* Packet type */
28 	u8  hlen;	/* Header length */
29 	u8  loff;	/* Data length offset in header */
30 	u8  lsize;	/* Data length field size */
31 	u16 maxlen;	/* Max overall packet length */
32 	int (*recv)(struct hci_dev *hdev, struct sk_buff *skb);
33 };
34 
35 #define H4_RECV_ACL \
36 	.type = HCI_ACLDATA_PKT, \
37 	.hlen = HCI_ACL_HDR_SIZE, \
38 	.loff = 2, \
39 	.lsize = 2, \
40 	.maxlen = HCI_MAX_FRAME_SIZE \
41 
42 #define H4_RECV_SCO \
43 	.type = HCI_SCODATA_PKT, \
44 	.hlen = HCI_SCO_HDR_SIZE, \
45 	.loff = 2, \
46 	.lsize = 1, \
47 	.maxlen = HCI_MAX_SCO_SIZE
48 
49 #define H4_RECV_EVENT \
50 	.type = HCI_EVENT_PKT, \
51 	.hlen = HCI_EVENT_HDR_SIZE, \
52 	.loff = 1, \
53 	.lsize = 1, \
54 	.maxlen = HCI_MAX_EVENT_SIZE
55 
56 static inline struct sk_buff *h4_recv_buf(struct hci_dev *hdev,
57 					  struct sk_buff *skb,
58 					  const unsigned char *buffer,
59 					  int count,
60 					  const struct h4_recv_pkt *pkts,
61 					  int pkts_count)
62 {
63 	while (count) {
64 		int i, len;
65 
66 		if (!count)
67 			break;
68 
69 		if (!skb) {
70 			for (i = 0; i < pkts_count; i++) {
71 				if (buffer[0] != (&pkts[i])->type)
72 					continue;
73 
74 				skb = bt_skb_alloc((&pkts[i])->maxlen,
75 						   GFP_ATOMIC);
76 				if (!skb)
77 					return ERR_PTR(-ENOMEM);
78 
79 				hci_skb_pkt_type(skb) = (&pkts[i])->type;
80 				hci_skb_expect(skb) = (&pkts[i])->hlen;
81 				break;
82 			}
83 
84 			/* Check for invalid packet type */
85 			if (!skb)
86 				return ERR_PTR(-EILSEQ);
87 
88 			count -= 1;
89 			buffer += 1;
90 		}
91 
92 		len = min_t(uint, hci_skb_expect(skb) - skb->len, count);
93 		skb_put_data(skb, buffer, len);
94 
95 		count -= len;
96 		buffer += len;
97 
98 		/* Check for partial packet */
99 		if (skb->len < hci_skb_expect(skb))
100 			continue;
101 
102 		for (i = 0; i < pkts_count; i++) {
103 			if (hci_skb_pkt_type(skb) == (&pkts[i])->type)
104 				break;
105 		}
106 
107 		if (i >= pkts_count) {
108 			kfree_skb(skb);
109 			return ERR_PTR(-EILSEQ);
110 		}
111 
112 		if (skb->len == (&pkts[i])->hlen) {
113 			u16 dlen;
114 
115 			switch ((&pkts[i])->lsize) {
116 			case 0:
117 				/* No variable data length */
118 				dlen = 0;
119 				break;
120 			case 1:
121 				/* Single octet variable length */
122 				dlen = skb->data[(&pkts[i])->loff];
123 				hci_skb_expect(skb) += dlen;
124 
125 				if (skb_tailroom(skb) < dlen) {
126 					kfree_skb(skb);
127 					return ERR_PTR(-EMSGSIZE);
128 				}
129 				break;
130 			case 2:
131 				/* Double octet variable length */
132 				dlen = get_unaligned_le16(skb->data +
133 							  (&pkts[i])->loff);
134 				hci_skb_expect(skb) += dlen;
135 
136 				if (skb_tailroom(skb) < dlen) {
137 					kfree_skb(skb);
138 					return ERR_PTR(-EMSGSIZE);
139 				}
140 				break;
141 			default:
142 				/* Unsupported variable length */
143 				kfree_skb(skb);
144 				return ERR_PTR(-EILSEQ);
145 			}
146 
147 			if (!dlen) {
148 				/* No more data, complete frame */
149 				(&pkts[i])->recv(hdev, skb);
150 				skb = NULL;
151 			}
152 		} else {
153 			/* Complete frame */
154 			(&pkts[i])->recv(hdev, skb);
155 			skb = NULL;
156 		}
157 	}
158 
159 	return skb;
160 }
161