Commit | Line | Data |
---|---|---|
07eb96a5 MH |
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 | { | |
1dc2d785 MJ |
63 | /* Check for error from previous call */ |
64 | if (IS_ERR(skb)) | |
65 | skb = NULL; | |
66 | ||
07eb96a5 MH |
67 | while (count) { |
68 | int i, len; | |
69 | ||
07eb96a5 MH |
70 | if (!skb) { |
71 | for (i = 0; i < pkts_count; i++) { | |
72 | if (buffer[0] != (&pkts[i])->type) | |
73 | continue; | |
74 | ||
75 | skb = bt_skb_alloc((&pkts[i])->maxlen, | |
76 | GFP_ATOMIC); | |
77 | if (!skb) | |
78 | return ERR_PTR(-ENOMEM); | |
79 | ||
80 | hci_skb_pkt_type(skb) = (&pkts[i])->type; | |
81 | hci_skb_expect(skb) = (&pkts[i])->hlen; | |
82 | break; | |
83 | } | |
84 | ||
85 | /* Check for invalid packet type */ | |
86 | if (!skb) | |
87 | return ERR_PTR(-EILSEQ); | |
88 | ||
89 | count -= 1; | |
90 | buffer += 1; | |
91 | } | |
92 | ||
93 | len = min_t(uint, hci_skb_expect(skb) - skb->len, count); | |
94 | skb_put_data(skb, buffer, len); | |
95 | ||
96 | count -= len; | |
97 | buffer += len; | |
98 | ||
99 | /* Check for partial packet */ | |
100 | if (skb->len < hci_skb_expect(skb)) | |
101 | continue; | |
102 | ||
103 | for (i = 0; i < pkts_count; i++) { | |
104 | if (hci_skb_pkt_type(skb) == (&pkts[i])->type) | |
105 | break; | |
106 | } | |
107 | ||
108 | if (i >= pkts_count) { | |
109 | kfree_skb(skb); | |
110 | return ERR_PTR(-EILSEQ); | |
111 | } | |
112 | ||
113 | if (skb->len == (&pkts[i])->hlen) { | |
114 | u16 dlen; | |
115 | ||
116 | switch ((&pkts[i])->lsize) { | |
117 | case 0: | |
118 | /* No variable data length */ | |
119 | dlen = 0; | |
120 | break; | |
121 | case 1: | |
122 | /* Single octet variable length */ | |
123 | dlen = skb->data[(&pkts[i])->loff]; | |
124 | hci_skb_expect(skb) += dlen; | |
125 | ||
126 | if (skb_tailroom(skb) < dlen) { | |
127 | kfree_skb(skb); | |
128 | return ERR_PTR(-EMSGSIZE); | |
129 | } | |
130 | break; | |
131 | case 2: | |
132 | /* Double octet variable length */ | |
133 | dlen = get_unaligned_le16(skb->data + | |
134 | (&pkts[i])->loff); | |
135 | hci_skb_expect(skb) += dlen; | |
136 | ||
137 | if (skb_tailroom(skb) < dlen) { | |
138 | kfree_skb(skb); | |
139 | return ERR_PTR(-EMSGSIZE); | |
140 | } | |
141 | break; | |
142 | default: | |
143 | /* Unsupported variable length */ | |
144 | kfree_skb(skb); | |
145 | return ERR_PTR(-EILSEQ); | |
146 | } | |
147 | ||
148 | if (!dlen) { | |
149 | /* No more data, complete frame */ | |
150 | (&pkts[i])->recv(hdev, skb); | |
151 | skb = NULL; | |
152 | } | |
153 | } else { | |
154 | /* Complete frame */ | |
155 | (&pkts[i])->recv(hdev, skb); | |
156 | skb = NULL; | |
157 | } | |
158 | } | |
159 | ||
160 | return skb; | |
161 | } |