]>
Commit | Line | Data |
---|---|---|
1a59d1b8 | 1 | // SPDX-License-Identifier: GPL-2.0-or-later |
1da177e4 | 2 | /* |
1da177e4 | 3 | * |
0372a662 MH |
4 | * Bluetooth HCI UART driver |
5 | * | |
6 | * Copyright (C) 2000-2001 Qualcomm Incorporated | |
7 | * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com> | |
8 | * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org> | |
1da177e4 | 9 | */ |
1da177e4 | 10 | |
1da177e4 LT |
11 | #include <linux/module.h> |
12 | ||
13 | #include <linux/kernel.h> | |
14 | #include <linux/init.h> | |
1da177e4 LT |
15 | #include <linux/types.h> |
16 | #include <linux/fcntl.h> | |
17 | #include <linux/interrupt.h> | |
18 | #include <linux/ptrace.h> | |
19 | #include <linux/poll.h> | |
20 | ||
21 | #include <linux/slab.h> | |
22 | #include <linux/tty.h> | |
23 | #include <linux/errno.h> | |
24 | #include <linux/string.h> | |
25 | #include <linux/signal.h> | |
26 | #include <linux/ioctl.h> | |
27 | #include <linux/skbuff.h> | |
79b8df93 | 28 | #include <asm/unaligned.h> |
1da177e4 LT |
29 | |
30 | #include <net/bluetooth/bluetooth.h> | |
31 | #include <net/bluetooth/hci_core.h> | |
0372a662 | 32 | |
1da177e4 | 33 | #include "hci_uart.h" |
1da177e4 | 34 | |
0372a662 | 35 | struct h4_struct { |
c27799f9 | 36 | struct sk_buff *rx_skb; |
0372a662 MH |
37 | struct sk_buff_head txq; |
38 | }; | |
39 | ||
1da177e4 LT |
40 | /* Initialize protocol */ |
41 | static int h4_open(struct hci_uart *hu) | |
42 | { | |
43 | struct h4_struct *h4; | |
0372a662 | 44 | |
1da177e4 | 45 | BT_DBG("hu %p", hu); |
0372a662 | 46 | |
fdcd1661 | 47 | h4 = kzalloc(sizeof(*h4), GFP_KERNEL); |
1da177e4 LT |
48 | if (!h4) |
49 | return -ENOMEM; | |
0372a662 | 50 | |
1da177e4 LT |
51 | skb_queue_head_init(&h4->txq); |
52 | ||
53 | hu->priv = h4; | |
54 | return 0; | |
55 | } | |
56 | ||
57 | /* Flush protocol data */ | |
58 | static int h4_flush(struct hci_uart *hu) | |
59 | { | |
60 | struct h4_struct *h4 = hu->priv; | |
61 | ||
62 | BT_DBG("hu %p", hu); | |
0372a662 | 63 | |
1da177e4 | 64 | skb_queue_purge(&h4->txq); |
0372a662 | 65 | |
1da177e4 LT |
66 | return 0; |
67 | } | |
68 | ||
69 | /* Close protocol */ | |
70 | static int h4_close(struct hci_uart *hu) | |
71 | { | |
72 | struct h4_struct *h4 = hu->priv; | |
0372a662 | 73 | |
1da177e4 LT |
74 | hu->priv = NULL; |
75 | ||
76 | BT_DBG("hu %p", hu); | |
77 | ||
78 | skb_queue_purge(&h4->txq); | |
0372a662 | 79 | |
c27799f9 MH |
80 | kfree_skb(h4->rx_skb); |
81 | ||
1da177e4 LT |
82 | hu->priv = NULL; |
83 | kfree(h4); | |
0372a662 | 84 | |
1da177e4 LT |
85 | return 0; |
86 | } | |
87 | ||
88 | /* Enqueue frame for transmittion (padding, crc, etc) */ | |
89 | static int h4_enqueue(struct hci_uart *hu, struct sk_buff *skb) | |
90 | { | |
91 | struct h4_struct *h4 = hu->priv; | |
92 | ||
93 | BT_DBG("hu %p skb %p", hu, skb); | |
94 | ||
95 | /* Prepend skb with frame type */ | |
618e8bc2 | 96 | memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); |
1da177e4 | 97 | skb_queue_tail(&h4->txq, skb); |
0372a662 | 98 | |
1da177e4 LT |
99 | return 0; |
100 | } | |
101 | ||
79b8df93 MH |
102 | static const struct h4_recv_pkt h4_recv_pkts[] = { |
103 | { H4_RECV_ACL, .recv = hci_recv_frame }, | |
104 | { H4_RECV_SCO, .recv = hci_recv_frame }, | |
105 | { H4_RECV_EVENT, .recv = hci_recv_frame }, | |
106 | }; | |
107 | ||
1da177e4 | 108 | /* Recv data */ |
9d1c40eb | 109 | static int h4_recv(struct hci_uart *hu, const void *data, int count) |
1da177e4 | 110 | { |
c27799f9 | 111 | struct h4_struct *h4 = hu->priv; |
b86ed368 | 112 | |
c2578202 CP |
113 | if (!test_bit(HCI_UART_REGISTERED, &hu->flags)) |
114 | return -EUNATCH; | |
115 | ||
79b8df93 MH |
116 | h4->rx_skb = h4_recv_buf(hu->hdev, h4->rx_skb, data, count, |
117 | h4_recv_pkts, ARRAY_SIZE(h4_recv_pkts)); | |
c27799f9 MH |
118 | if (IS_ERR(h4->rx_skb)) { |
119 | int err = PTR_ERR(h4->rx_skb); | |
2064ee33 | 120 | bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err); |
37134167 | 121 | h4->rx_skb = NULL; |
c27799f9 | 122 | return err; |
b86ed368 | 123 | } |
0372a662 | 124 | |
1da177e4 LT |
125 | return count; |
126 | } | |
127 | ||
128 | static struct sk_buff *h4_dequeue(struct hci_uart *hu) | |
129 | { | |
130 | struct h4_struct *h4 = hu->priv; | |
131 | return skb_dequeue(&h4->txq); | |
132 | } | |
133 | ||
4ee7ef19 | 134 | static const struct hci_uart_proto h4p = { |
0372a662 | 135 | .id = HCI_UART_H4, |
7c40fb8d | 136 | .name = "H4", |
0372a662 MH |
137 | .open = h4_open, |
138 | .close = h4_close, | |
139 | .recv = h4_recv, | |
140 | .enqueue = h4_enqueue, | |
141 | .dequeue = h4_dequeue, | |
142 | .flush = h4_flush, | |
1da177e4 | 143 | }; |
0372a662 | 144 | |
f2b94bb9 | 145 | int __init h4_init(void) |
1da177e4 | 146 | { |
01009eec | 147 | return hci_uart_register_proto(&h4p); |
1da177e4 LT |
148 | } |
149 | ||
f2b94bb9 | 150 | int __exit h4_deinit(void) |
1da177e4 LT |
151 | { |
152 | return hci_uart_unregister_proto(&h4p); | |
153 | } | |
e1a38d70 MH |
154 | |
155 | struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb, | |
79b8df93 MH |
156 | const unsigned char *buffer, int count, |
157 | const struct h4_recv_pkt *pkts, int pkts_count) | |
e1a38d70 | 158 | { |
aeac3014 | 159 | struct hci_uart *hu = hci_get_drvdata(hdev); |
fb776481 | 160 | u8 alignment = hu->alignment ? hu->alignment : 1; |
aeac3014 | 161 | |
1dc2d785 MJ |
162 | /* Check for error from previous call */ |
163 | if (IS_ERR(skb)) | |
164 | skb = NULL; | |
165 | ||
e1a38d70 | 166 | while (count) { |
79b8df93 | 167 | int i, len; |
e1a38d70 | 168 | |
aeac3014 SR |
169 | /* remove padding bytes from buffer */ |
170 | for (; hu->padding && count > 0; hu->padding--) { | |
171 | count--; | |
172 | buffer++; | |
173 | } | |
174 | if (!count) | |
175 | break; | |
176 | ||
e1a38d70 | 177 | if (!skb) { |
79b8df93 MH |
178 | for (i = 0; i < pkts_count; i++) { |
179 | if (buffer[0] != (&pkts[i])->type) | |
180 | continue; | |
e1a38d70 | 181 | |
79b8df93 | 182 | skb = bt_skb_alloc((&pkts[i])->maxlen, |
e1a38d70 MH |
183 | GFP_ATOMIC); |
184 | if (!skb) | |
185 | return ERR_PTR(-ENOMEM); | |
186 | ||
618e8bc2 MH |
187 | hci_skb_pkt_type(skb) = (&pkts[i])->type; |
188 | hci_skb_expect(skb) = (&pkts[i])->hlen; | |
e1a38d70 | 189 | break; |
79b8df93 | 190 | } |
e1a38d70 | 191 | |
79b8df93 MH |
192 | /* Check for invalid packet type */ |
193 | if (!skb) | |
e1a38d70 | 194 | return ERR_PTR(-EILSEQ); |
e1a38d70 MH |
195 | |
196 | count -= 1; | |
197 | buffer += 1; | |
198 | } | |
199 | ||
618e8bc2 | 200 | len = min_t(uint, hci_skb_expect(skb) - skb->len, count); |
59ae1d12 | 201 | skb_put_data(skb, buffer, len); |
e1a38d70 MH |
202 | |
203 | count -= len; | |
204 | buffer += len; | |
e1a38d70 | 205 | |
79b8df93 | 206 | /* Check for partial packet */ |
618e8bc2 | 207 | if (skb->len < hci_skb_expect(skb)) |
79b8df93 MH |
208 | continue; |
209 | ||
210 | for (i = 0; i < pkts_count; i++) { | |
618e8bc2 | 211 | if (hci_skb_pkt_type(skb) == (&pkts[i])->type) |
79b8df93 MH |
212 | break; |
213 | } | |
214 | ||
215 | if (i >= pkts_count) { | |
216 | kfree_skb(skb); | |
217 | return ERR_PTR(-EILSEQ); | |
218 | } | |
e1a38d70 | 219 | |
79b8df93 MH |
220 | if (skb->len == (&pkts[i])->hlen) { |
221 | u16 dlen; | |
e1a38d70 | 222 | |
79b8df93 MH |
223 | switch ((&pkts[i])->lsize) { |
224 | case 0: | |
225 | /* No variable data length */ | |
e5499167 | 226 | dlen = 0; |
79b8df93 MH |
227 | break; |
228 | case 1: | |
229 | /* Single octet variable length */ | |
230 | dlen = skb->data[(&pkts[i])->loff]; | |
618e8bc2 | 231 | hci_skb_expect(skb) += dlen; |
e1a38d70 | 232 | |
79b8df93 | 233 | if (skb_tailroom(skb) < dlen) { |
e1a38d70 MH |
234 | kfree_skb(skb); |
235 | return ERR_PTR(-EMSGSIZE); | |
236 | } | |
79b8df93 MH |
237 | break; |
238 | case 2: | |
239 | /* Double octet variable length */ | |
240 | dlen = get_unaligned_le16(skb->data + | |
241 | (&pkts[i])->loff); | |
618e8bc2 | 242 | hci_skb_expect(skb) += dlen; |
e1a38d70 | 243 | |
79b8df93 | 244 | if (skb_tailroom(skb) < dlen) { |
e1a38d70 MH |
245 | kfree_skb(skb); |
246 | return ERR_PTR(-EMSGSIZE); | |
247 | } | |
79b8df93 MH |
248 | break; |
249 | default: | |
250 | /* Unsupported variable length */ | |
251 | kfree_skb(skb); | |
252 | return ERR_PTR(-EILSEQ); | |
e1a38d70 | 253 | } |
e5499167 LP |
254 | |
255 | if (!dlen) { | |
aeac3014 SR |
256 | hu->padding = (skb->len - 1) % alignment; |
257 | hu->padding = (alignment - hu->padding) % alignment; | |
258 | ||
e5499167 LP |
259 | /* No more data, complete frame */ |
260 | (&pkts[i])->recv(hdev, skb); | |
261 | skb = NULL; | |
262 | } | |
79b8df93 | 263 | } else { |
aeac3014 SR |
264 | hu->padding = (skb->len - 1) % alignment; |
265 | hu->padding = (alignment - hu->padding) % alignment; | |
266 | ||
e1a38d70 | 267 | /* Complete frame */ |
79b8df93 | 268 | (&pkts[i])->recv(hdev, skb); |
e1a38d70 MH |
269 | skb = NULL; |
270 | } | |
271 | } | |
272 | ||
273 | return skb; | |
274 | } | |
943cc592 | 275 | EXPORT_SYMBOL_GPL(h4_recv_buf); |