blob: ce82722d049b7c013fd06f66f726fbbf75a01f56 [file] [log] [blame]
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +09001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 BNEP implementation for Linux Bluetooth stack (BlueZ).
3 Copyright (C) 2001-2002 Inventel Systemes
4 Written 2001-2002 by
Jan Engelhardt96de0e22007-10-19 23:21:04 +02005 Clément Moreau <clement.moreau@inventel.fr>
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 David Libault <david.libault@inventel.fr>
7
8 Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
9
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License version 2 as
12 published by the Free Software Foundation;
13
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
17 IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090018 CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
19 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
Linus Torvalds1da177e2005-04-16 15:20:36 -070021 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090023 ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
24 COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 SOFTWARE IS DISCLAIMED.
26*/
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/module.h>
Szymon Jancf4d7cd42011-03-21 14:20:00 +010029#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#include <linux/file.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031#include <linux/etherdevice.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#include <asm/unaligned.h>
33
34#include <net/bluetooth/bluetooth.h>
Marcel Holtmann65f53e92013-10-13 09:49:57 -070035#include <net/bluetooth/l2cap.h>
Marcel Holtmann0a85b962006-07-06 13:09:02 +020036#include <net/bluetooth/hci_core.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
38#include "bnep.h"
39
Marcel Holtmann28111eb2008-08-07 22:26:54 +020040#define VERSION "1.3"
41
Rusty Russelleb939922011-12-19 14:08:01 +000042static bool compress_src = true;
43static bool compress_dst = true;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
45static LIST_HEAD(bnep_session_list);
46static DECLARE_RWSEM(bnep_session_sem);
47
48static struct bnep_session *__bnep_get_session(u8 *dst)
49{
50 struct bnep_session *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051
52 BT_DBG("");
53
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +020054 list_for_each_entry(s, &bnep_session_list, list)
Joe Perchesc47fc982012-05-08 18:56:51 +000055 if (ether_addr_equal(dst, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 return s;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +020057
Linus Torvalds1da177e2005-04-16 15:20:36 -070058 return NULL;
59}
60
61static void __bnep_link_session(struct bnep_session *s)
62{
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090063 list_add(&s->list, &bnep_session_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064}
65
66static void __bnep_unlink_session(struct bnep_session *s)
67{
68 list_del(&s->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070069}
70
71static int bnep_send(struct bnep_session *s, void *data, size_t len)
72{
73 struct socket *sock = s->sock;
74 struct kvec iv = { data, len };
75
76 return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
77}
78
79static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
80{
81 struct bnep_control_rsp rsp;
82 rsp.type = BNEP_CONTROL;
83 rsp.ctrl = ctrl;
84 rsp.resp = htons(resp);
85 return bnep_send(s, &rsp, sizeof(rsp));
86}
87
88#ifdef CONFIG_BT_BNEP_PROTO_FILTER
89static inline void bnep_set_default_proto_filter(struct bnep_session *s)
90{
91 /* (IPv4, ARP) */
Al Viroe41d2162006-11-08 00:27:36 -080092 s->proto_filter[0].start = ETH_P_IP;
93 s->proto_filter[0].end = ETH_P_ARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 /* (RARP, AppleTalk) */
Al Viroe41d2162006-11-08 00:27:36 -080095 s->proto_filter[1].start = ETH_P_RARP;
96 s->proto_filter[1].end = ETH_P_AARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -070097 /* (IPX, IPv6) */
Al Viroe41d2162006-11-08 00:27:36 -080098 s->proto_filter[2].start = ETH_P_IPX;
99 s->proto_filter[2].end = ETH_P_IPV6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700100}
101#endif
102
Al Viro1bc5d442006-11-08 00:27:57 -0800103static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700104{
105 int n;
106
107 if (len < 2)
108 return -EILSEQ;
109
Harvey Harrison83985312008-05-02 16:25:46 -0700110 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100111 data++;
112 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113
114 if (len < n)
115 return -EILSEQ;
116
117 BT_DBG("filter len %d", n);
118
119#ifdef CONFIG_BT_BNEP_PROTO_FILTER
120 n /= 4;
121 if (n <= BNEP_MAX_PROTO_FILTERS) {
122 struct bnep_proto_filter *f = s->proto_filter;
123 int i;
124
125 for (i = 0; i < n; i++) {
Harvey Harrison83985312008-05-02 16:25:46 -0700126 f[i].start = get_unaligned_be16(data++);
127 f[i].end = get_unaligned_be16(data++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700128
129 BT_DBG("proto filter start %d end %d",
130 f[i].start, f[i].end);
131 }
132
133 if (i < BNEP_MAX_PROTO_FILTERS)
134 memset(f + i, 0, sizeof(*f));
135
136 if (n == 0)
137 bnep_set_default_proto_filter(s);
138
139 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
140 } else {
141 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
142 }
143#else
144 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
145#endif
146 return 0;
147}
148
149static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
150{
151 int n;
152
153 if (len < 2)
154 return -EILSEQ;
155
Harvey Harrison83985312008-05-02 16:25:46 -0700156 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100157 data += 2;
158 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700159
160 if (len < n)
161 return -EILSEQ;
162
163 BT_DBG("filter len %d", n);
164
165#ifdef CONFIG_BT_BNEP_MC_FILTER
166 n /= (ETH_ALEN * 2);
167
168 if (n > 0) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100169 int i;
170
Linus Torvalds1da177e2005-04-16 15:20:36 -0700171 s->mc_filter = 0;
172
173 /* Always send broadcast */
174 set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
175
176 /* Add address ranges to the multicast hash */
177 for (; n > 0; n--) {
178 u8 a1[6], *a2;
179
Szymon Janc3aad75a2011-03-21 14:19:58 +0100180 memcpy(a1, data, ETH_ALEN);
181 data += ETH_ALEN;
182 a2 = data;
183 data += ETH_ALEN;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900184
Andrei Emeltchenko6ed93dc2012-09-25 12:49:43 +0300185 BT_DBG("mc filter %pMR -> %pMR", a1, a2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187 /* Iterate from a1 to a2 */
188 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
189 while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100190 /* Increment a1 */
191 i = 5;
192 while (i >= 0 && ++a1[i--] == 0)
193 ;
194
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
196 }
197 }
198 }
199
200 BT_DBG("mc filter hash 0x%llx", s->mc_filter);
201
202 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
203#else
204 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
205#endif
206 return 0;
207}
208
209static int bnep_rx_control(struct bnep_session *s, void *data, int len)
210{
211 u8 cmd = *(u8 *)data;
212 int err = 0;
213
Szymon Janc3aad75a2011-03-21 14:19:58 +0100214 data++;
215 len--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
217 switch (cmd) {
218 case BNEP_CMD_NOT_UNDERSTOOD:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700219 case BNEP_SETUP_CONN_RSP:
220 case BNEP_FILTER_NET_TYPE_RSP:
221 case BNEP_FILTER_MULTI_ADDR_RSP:
222 /* Ignore these for now */
223 break;
224
225 case BNEP_FILTER_NET_TYPE_SET:
226 err = bnep_ctrl_set_netfilter(s, data, len);
227 break;
228
229 case BNEP_FILTER_MULTI_ADDR_SET:
230 err = bnep_ctrl_set_mcfilter(s, data, len);
231 break;
232
Vikram Kandukuricde9f802009-12-03 15:12:51 +0530233 case BNEP_SETUP_CONN_REQ:
234 err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
235 break;
236
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 default: {
238 u8 pkt[3];
239 pkt[0] = BNEP_CONTROL;
240 pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
241 pkt[2] = cmd;
242 bnep_send(s, pkt, sizeof(pkt));
243 }
244 break;
245 }
246
247 return err;
248}
249
250static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
251{
252 struct bnep_ext_hdr *h;
253 int err = 0;
254
255 do {
256 h = (void *) skb->data;
257 if (!skb_pull(skb, sizeof(*h))) {
258 err = -EILSEQ;
259 break;
260 }
261
262 BT_DBG("type 0x%x len %d", h->type, h->len);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 switch (h->type & BNEP_TYPE_MASK) {
265 case BNEP_EXT_CONTROL:
266 bnep_rx_control(s, skb->data, skb->len);
267 break;
268
269 default:
270 /* Unknown extension, skip it. */
271 break;
272 }
273
274 if (!skb_pull(skb, h->len)) {
275 err = -EILSEQ;
276 break;
277 }
278 } while (!err && (h->type & BNEP_EXT_HEADER));
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900279
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 return err;
281}
282
283static u8 __bnep_rx_hlen[] = {
284 ETH_HLEN, /* BNEP_GENERAL */
285 0, /* BNEP_CONTROL */
286 2, /* BNEP_COMPRESSED */
287 ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
288 ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
289};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Gustavo Padovan6039aa732012-05-23 04:04:18 -0300291static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292{
293 struct net_device *dev = s->dev;
294 struct sk_buff *nskb;
295 u8 type;
296
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800297 dev->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
Szymon Janc3aad75a2011-03-21 14:19:58 +0100299 type = *(u8 *) skb->data;
300 skb_pull(skb, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301
Szymon Janca3d9bd42011-03-21 14:19:57 +0100302 if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 goto badframe;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900304
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
306 bnep_rx_control(s, skb->data, skb->len);
307 kfree_skb(skb);
308 return 0;
309 }
310
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700311 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
313 /* Verify and pull out header */
314 if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
315 goto badframe;
316
Al Viro1bc5d442006-11-08 00:27:57 -0800317 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 if (type & BNEP_EXT_HEADER) {
320 if (bnep_rx_extension(s, skb) < 0)
321 goto badframe;
322 }
323
324 /* Strip 802.1p header */
Eldad Zack000092b2012-05-08 00:09:35 +0200325 if (ntohs(s->eh.h_proto) == ETH_P_8021Q) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 if (!skb_pull(skb, 4))
327 goto badframe;
Al Viro1bc5d442006-11-08 00:27:57 -0800328 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 }
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 /* We have to alloc new skb and copy data here :(. Because original skb
332 * may not be modified and because of the alignment requirements. */
333 nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
334 if (!nskb) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800335 dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336 kfree_skb(skb);
337 return -ENOMEM;
338 }
339 skb_reserve(nskb, 2);
340
341 /* Decompress header and construct ether frame */
342 switch (type & BNEP_TYPE_MASK) {
343 case BNEP_COMPRESSED:
344 memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
345 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900346
Linus Torvalds1da177e2005-04-16 15:20:36 -0700347 case BNEP_COMPRESSED_SRC_ONLY:
348 memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700349 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
Al Viro1bc5d442006-11-08 00:27:57 -0800350 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 break;
352
353 case BNEP_COMPRESSED_DST_ONLY:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700354 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100355 ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700356 memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
Szymon Janc3aad75a2011-03-21 14:19:58 +0100357 ETH_ALEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700358 break;
359
360 case BNEP_GENERAL:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700361 memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100362 ETH_ALEN * 2);
Al Viro1bc5d442006-11-08 00:27:57 -0800363 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700364 break;
365 }
366
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300367 skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 kfree_skb(skb);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900369
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800370 dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 nskb->ip_summed = CHECKSUM_NONE;
372 nskb->protocol = eth_type_trans(nskb, dev);
373 netif_rx_ni(nskb);
374 return 0;
375
376badframe:
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800377 dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 kfree_skb(skb);
379 return 0;
380}
381
382static u8 __bnep_tx_types[] = {
383 BNEP_GENERAL,
384 BNEP_COMPRESSED_SRC_ONLY,
385 BNEP_COMPRESSED_DST_ONLY,
386 BNEP_COMPRESSED
387};
388
Gustavo Padovan6039aa732012-05-23 04:04:18 -0300389static int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390{
391 struct ethhdr *eh = (void *) skb->data;
392 struct socket *sock = s->sock;
393 struct kvec iv[3];
394 int len = 0, il = 0;
395 u8 type = 0;
396
397 BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
398
399 if (!skb->dev) {
400 /* Control frame sent by us */
401 goto send;
402 }
403
404 iv[il++] = (struct kvec) { &type, 1 };
405 len++;
406
Joe Perchesc47fc982012-05-08 18:56:51 +0000407 if (compress_src && ether_addr_equal(eh->h_dest, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 type |= 0x01;
409
Joe Perchesc47fc982012-05-08 18:56:51 +0000410 if (compress_dst && ether_addr_equal(eh->h_source, s->eh.h_dest))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 type |= 0x02;
412
413 if (type)
414 skb_pull(skb, ETH_ALEN * 2);
415
416 type = __bnep_tx_types[type];
417 switch (type) {
418 case BNEP_COMPRESSED_SRC_ONLY:
419 iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
420 len += ETH_ALEN;
421 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900422
Linus Torvalds1da177e2005-04-16 15:20:36 -0700423 case BNEP_COMPRESSED_DST_ONLY:
424 iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
425 len += ETH_ALEN;
426 break;
427 }
428
429send:
430 iv[il++] = (struct kvec) { skb->data, skb->len };
431 len += skb->len;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900432
Linus Torvalds1da177e2005-04-16 15:20:36 -0700433 /* FIXME: linearize skb */
434 {
435 len = kernel_sendmsg(sock, &s->msg, iv, il, len);
436 }
437 kfree_skb(skb);
438
439 if (len > 0) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800440 s->dev->stats.tx_bytes += len;
441 s->dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700442 return 0;
443 }
444
445 return len;
446}
447
448static int bnep_session(void *arg)
449{
450 struct bnep_session *s = arg;
451 struct net_device *dev = s->dev;
452 struct sock *sk = s->sock->sk;
453 struct sk_buff *skb;
454 wait_queue_t wait;
455
456 BT_DBG("");
457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459
460 init_waitqueue_entry(&wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000461 add_wait_queue(sk_sleep(sk), &wait);
Peter Hurley38d57552011-07-24 00:11:07 -0400462 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 set_current_state(TASK_INTERRUPTIBLE);
464
Peter Hurley751c10a2011-08-05 10:41:35 -0400465 if (atomic_read(&s->terminate))
Peter Hurley38d57552011-07-24 00:11:07 -0400466 break;
Szymon Janc3aad75a2011-03-21 14:19:58 +0100467 /* RX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
469 skb_orphan(skb);
Mat Martineau44935722011-07-22 14:53:58 -0700470 if (!skb_linearize(skb))
471 bnep_rx_frame(s, skb);
472 else
473 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 }
475
476 if (sk->sk_state != BT_CONNECTED)
477 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900478
Szymon Janc3aad75a2011-03-21 14:19:58 +0100479 /* TX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480 while ((skb = skb_dequeue(&sk->sk_write_queue)))
481 if (bnep_tx_frame(s, skb))
482 break;
483 netif_wake_queue(dev);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900484
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 schedule();
486 }
Peter Hurley38d57552011-07-24 00:11:07 -0400487 __set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000488 remove_wait_queue(sk_sleep(sk), &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489
490 /* Cleanup session */
491 down_write(&bnep_session_sem);
492
493 /* Delete network device */
494 unregister_netdev(dev);
495
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200496 /* Wakeup user-space polling for socket errors */
497 s->sock->sk->sk_err = EUNATCH;
498
Eric Dumazetaa395142010-04-20 13:03:51 +0000499 wake_up_interruptible(sk_sleep(s->sock->sk));
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200500
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501 /* Release the socket */
502 fput(s->sock->file);
503
504 __bnep_unlink_session(s);
505
506 up_write(&bnep_session_sem);
507 free_netdev(dev);
David Herrmann9b338c32011-11-19 13:23:33 +0100508 module_put_and_exit(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509 return 0;
510}
511
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200512static struct device *bnep_get_device(struct bnep_session *session)
513{
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200514 struct hci_conn *conn;
515
Marcel Holtmann65f53e92013-10-13 09:49:57 -0700516 conn = l2cap_pi(session->sock->sk)->chan->conn->hcon;
517 if (!conn)
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200518 return NULL;
519
Marcel Holtmann65f53e92013-10-13 09:49:57 -0700520 return &conn->dev;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200521}
522
Marcel Holtmann384912e2009-08-31 21:08:19 +0000523static struct device_type bnep_type = {
524 .name = "bluetooth",
525};
526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
528{
529 struct net_device *dev;
530 struct bnep_session *s, *ss;
531 u8 dst[ETH_ALEN], src[ETH_ALEN];
532 int err;
533
534 BT_DBG("");
535
Al Viro71bb99a2014-12-19 06:20:59 +0000536 if (!l2cap_is_socket(sock))
537 return -EBADFD;
538
Marcel Holtmann65f53e92013-10-13 09:49:57 -0700539 baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
540 baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541
542 /* session struct allocated as private part of net_device */
543 dev = alloc_netdev(sizeof(struct bnep_session),
Tom Gundersenc835a672014-07-14 16:37:24 +0200544 (*req->device) ? req->device : "bnep%d",
545 NET_NAME_UNKNOWN,
546 bnep_net_setup);
Tobias Klauser67b52e52006-03-21 23:53:16 -0800547 if (!dev)
548 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Linus Torvalds1da177e2005-04-16 15:20:36 -0700550 down_write(&bnep_session_sem);
551
552 ss = __bnep_get_session(dst);
553 if (ss && ss->state == BT_CONNECTED) {
554 err = -EEXIST;
555 goto failed;
556 }
557
Wang Chen524ad0a2008-11-12 23:39:10 -0800558 s = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559
560 /* This is rx header therefore addresses are swapped.
Szymon Janc3aad75a2011-03-21 14:19:58 +0100561 * ie. eh.h_dest is our local address. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700562 memcpy(s->eh.h_dest, &src, ETH_ALEN);
563 memcpy(s->eh.h_source, &dst, ETH_ALEN);
564 memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
565
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200566 s->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 s->sock = sock;
568 s->role = req->role;
569 s->state = BT_CONNECTED;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900570
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 s->msg.msg_flags = MSG_NOSIGNAL;
572
573#ifdef CONFIG_BT_BNEP_MC_FILTER
574 /* Set default mc filter */
575 set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
576#endif
577
578#ifdef CONFIG_BT_BNEP_PROTO_FILTER
579 /* Set default protocol filter */
580 bnep_set_default_proto_filter(s);
581#endif
582
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200583 SET_NETDEV_DEV(dev, bnep_get_device(s));
Marcel Holtmann384912e2009-08-31 21:08:19 +0000584 SET_NETDEV_DEVTYPE(dev, &bnep_type);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200585
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 err = register_netdev(dev);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100587 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 __bnep_link_session(s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900591
David Herrmann9b338c32011-11-19 13:23:33 +0100592 __module_get(THIS_MODULE);
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100593 s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
594 if (IS_ERR(s->task)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 /* Session thread start failed, gotta cleanup. */
David Herrmann9b338c32011-11-19 13:23:33 +0100596 module_put(THIS_MODULE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700597 unregister_netdev(dev);
598 __bnep_unlink_session(s);
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100599 err = PTR_ERR(s->task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 goto failed;
601 }
602
603 up_write(&bnep_session_sem);
604 strcpy(req->device, dev->name);
605 return 0;
606
607failed:
608 up_write(&bnep_session_sem);
609 free_netdev(dev);
610 return err;
611}
612
613int bnep_del_connection(struct bnep_conndel_req *req)
614{
615 struct bnep_session *s;
616 int err = 0;
617
618 BT_DBG("");
619
620 down_read(&bnep_session_sem);
621
622 s = __bnep_get_session(req->dst);
Peter Hurley751c10a2011-08-05 10:41:35 -0400623 if (s) {
624 atomic_inc(&s->terminate);
625 wake_up_process(s->task);
626 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 err = -ENOENT;
628
629 up_read(&bnep_session_sem);
630 return err;
631}
632
633static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
634{
Vasiliy Kulikov5520d202010-10-30 18:26:21 +0400635 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
637 strcpy(ci->device, s->dev->name);
638 ci->flags = s->flags;
639 ci->state = s->state;
640 ci->role = s->role;
641}
642
643int bnep_get_connlist(struct bnep_connlist_req *req)
644{
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200645 struct bnep_session *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 int err = 0, n = 0;
647
648 down_read(&bnep_session_sem);
649
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200650 list_for_each_entry(s, &bnep_session_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 struct bnep_conninfo ci;
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 __bnep_copy_ci(&ci, s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900654
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
656 err = -EFAULT;
657 break;
658 }
659
660 if (++n >= req->cnum)
661 break;
662
663 req->ci++;
664 }
665 req->cnum = n;
666
667 up_read(&bnep_session_sem);
668 return err;
669}
670
671int bnep_get_conninfo(struct bnep_conninfo *ci)
672{
673 struct bnep_session *s;
674 int err = 0;
675
676 down_read(&bnep_session_sem);
677
678 s = __bnep_get_session(ci->dst);
679 if (s)
680 __bnep_copy_ci(ci, s);
681 else
682 err = -ENOENT;
683
684 up_read(&bnep_session_sem);
685 return err;
686}
687
688static int __init bnep_init(void)
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900689{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 char flt[50] = "";
691
Linus Torvalds1da177e2005-04-16 15:20:36 -0700692#ifdef CONFIG_BT_BNEP_PROTO_FILTER
693 strcat(flt, "protocol ");
694#endif
695
696#ifdef CONFIG_BT_BNEP_MC_FILTER
697 strcat(flt, "multicast");
698#endif
699
700 BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
701 if (flt[0])
702 BT_INFO("BNEP filters: %s", flt);
703
704 bnep_sock_init();
705 return 0;
706}
707
708static void __exit bnep_exit(void)
709{
710 bnep_sock_cleanup();
711}
712
713module_init(bnep_init);
714module_exit(bnep_exit);
715
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200716module_param(compress_src, bool, 0644);
717MODULE_PARM_DESC(compress_src, "Compress sources headers");
718
719module_param(compress_dst, bool, 0644);
720MODULE_PARM_DESC(compress_dst, "Compress destination headers");
721
Marcel Holtmann63fbd242008-08-18 13:23:53 +0200722MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
724MODULE_VERSION(VERSION);
725MODULE_LICENSE("GPL");
726MODULE_ALIAS("bt-proto-4");