blob: 1eea8208b2cc760fc36bd56b8531ba81faccc481 [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>
29
30#include <linux/kernel.h>
31#include <linux/sched.h>
32#include <linux/signal.h>
33#include <linux/init.h>
34#include <linux/wait.h>
Rafael J. Wysocki83144182007-07-17 04:03:35 -070035#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/net.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090038#include <linux/slab.h>
Szymon Jancf4d7cd42011-03-21 14:20:00 +010039#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040#include <net/sock.h>
41
42#include <linux/socket.h>
43#include <linux/file.h>
44
45#include <linux/netdevice.h>
46#include <linux/etherdevice.h>
47#include <linux/skbuff.h>
48
49#include <asm/unaligned.h>
50
51#include <net/bluetooth/bluetooth.h>
Marcel Holtmann0a85b962006-07-06 13:09:02 +020052#include <net/bluetooth/hci_core.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070053#include <net/bluetooth/l2cap.h>
54
55#include "bnep.h"
56
Marcel Holtmann28111eb2008-08-07 22:26:54 +020057#define VERSION "1.3"
58
59static int compress_src = 1;
60static int compress_dst = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070061
62static LIST_HEAD(bnep_session_list);
63static DECLARE_RWSEM(bnep_session_sem);
64
65static struct bnep_session *__bnep_get_session(u8 *dst)
66{
67 struct bnep_session *s;
68 struct list_head *p;
69
70 BT_DBG("");
71
72 list_for_each(p, &bnep_session_list) {
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090073 s = list_entry(p, struct bnep_session, list);
Kris Katterjohnd3f4a682006-01-09 16:01:43 -080074 if (!compare_ether_addr(dst, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 return s;
76 }
77 return NULL;
78}
79
80static void __bnep_link_session(struct bnep_session *s)
81{
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090082 list_add(&s->list, &bnep_session_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083}
84
85static void __bnep_unlink_session(struct bnep_session *s)
86{
87 list_del(&s->list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
90static int bnep_send(struct bnep_session *s, void *data, size_t len)
91{
92 struct socket *sock = s->sock;
93 struct kvec iv = { data, len };
94
95 return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
96}
97
98static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
99{
100 struct bnep_control_rsp rsp;
101 rsp.type = BNEP_CONTROL;
102 rsp.ctrl = ctrl;
103 rsp.resp = htons(resp);
104 return bnep_send(s, &rsp, sizeof(rsp));
105}
106
107#ifdef CONFIG_BT_BNEP_PROTO_FILTER
108static inline void bnep_set_default_proto_filter(struct bnep_session *s)
109{
110 /* (IPv4, ARP) */
Al Viroe41d2162006-11-08 00:27:36 -0800111 s->proto_filter[0].start = ETH_P_IP;
112 s->proto_filter[0].end = ETH_P_ARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 /* (RARP, AppleTalk) */
Al Viroe41d2162006-11-08 00:27:36 -0800114 s->proto_filter[1].start = ETH_P_RARP;
115 s->proto_filter[1].end = ETH_P_AARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 /* (IPX, IPv6) */
Al Viroe41d2162006-11-08 00:27:36 -0800117 s->proto_filter[2].start = ETH_P_IPX;
118 s->proto_filter[2].end = ETH_P_IPV6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119}
120#endif
121
Al Viro1bc5d442006-11-08 00:27:57 -0800122static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123{
124 int n;
125
126 if (len < 2)
127 return -EILSEQ;
128
Harvey Harrison83985312008-05-02 16:25:46 -0700129 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100130 data++;
131 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700132
133 if (len < n)
134 return -EILSEQ;
135
136 BT_DBG("filter len %d", n);
137
138#ifdef CONFIG_BT_BNEP_PROTO_FILTER
139 n /= 4;
140 if (n <= BNEP_MAX_PROTO_FILTERS) {
141 struct bnep_proto_filter *f = s->proto_filter;
142 int i;
143
144 for (i = 0; i < n; i++) {
Harvey Harrison83985312008-05-02 16:25:46 -0700145 f[i].start = get_unaligned_be16(data++);
146 f[i].end = get_unaligned_be16(data++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147
148 BT_DBG("proto filter start %d end %d",
149 f[i].start, f[i].end);
150 }
151
152 if (i < BNEP_MAX_PROTO_FILTERS)
153 memset(f + i, 0, sizeof(*f));
154
155 if (n == 0)
156 bnep_set_default_proto_filter(s);
157
158 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
159 } else {
160 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
161 }
162#else
163 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
164#endif
165 return 0;
166}
167
168static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
169{
170 int n;
171
172 if (len < 2)
173 return -EILSEQ;
174
Harvey Harrison83985312008-05-02 16:25:46 -0700175 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100176 data += 2;
177 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178
179 if (len < n)
180 return -EILSEQ;
181
182 BT_DBG("filter len %d", n);
183
184#ifdef CONFIG_BT_BNEP_MC_FILTER
185 n /= (ETH_ALEN * 2);
186
187 if (n > 0) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100188 int i;
189
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 s->mc_filter = 0;
191
192 /* Always send broadcast */
193 set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
194
195 /* Add address ranges to the multicast hash */
196 for (; n > 0; n--) {
197 u8 a1[6], *a2;
198
Szymon Janc3aad75a2011-03-21 14:19:58 +0100199 memcpy(a1, data, ETH_ALEN);
200 data += ETH_ALEN;
201 a2 = data;
202 data += ETH_ALEN;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900203
Linus Torvalds1da177e2005-04-16 15:20:36 -0700204 BT_DBG("mc filter %s -> %s",
205 batostr((void *) a1), batostr((void *) a2));
206
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 /* Iterate from a1 to a2 */
208 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
209 while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100210 /* Increment a1 */
211 i = 5;
212 while (i >= 0 && ++a1[i--] == 0)
213 ;
214
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
216 }
217 }
218 }
219
220 BT_DBG("mc filter hash 0x%llx", s->mc_filter);
221
222 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
223#else
224 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
225#endif
226 return 0;
227}
228
229static int bnep_rx_control(struct bnep_session *s, void *data, int len)
230{
231 u8 cmd = *(u8 *)data;
232 int err = 0;
233
Szymon Janc3aad75a2011-03-21 14:19:58 +0100234 data++;
235 len--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
237 switch (cmd) {
238 case BNEP_CMD_NOT_UNDERSTOOD:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239 case BNEP_SETUP_CONN_RSP:
240 case BNEP_FILTER_NET_TYPE_RSP:
241 case BNEP_FILTER_MULTI_ADDR_RSP:
242 /* Ignore these for now */
243 break;
244
245 case BNEP_FILTER_NET_TYPE_SET:
246 err = bnep_ctrl_set_netfilter(s, data, len);
247 break;
248
249 case BNEP_FILTER_MULTI_ADDR_SET:
250 err = bnep_ctrl_set_mcfilter(s, data, len);
251 break;
252
Vikram Kandukuricde9f802009-12-03 15:12:51 +0530253 case BNEP_SETUP_CONN_REQ:
254 err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
255 break;
256
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257 default: {
258 u8 pkt[3];
259 pkt[0] = BNEP_CONTROL;
260 pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
261 pkt[2] = cmd;
262 bnep_send(s, pkt, sizeof(pkt));
263 }
264 break;
265 }
266
267 return err;
268}
269
270static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
271{
272 struct bnep_ext_hdr *h;
273 int err = 0;
274
275 do {
276 h = (void *) skb->data;
277 if (!skb_pull(skb, sizeof(*h))) {
278 err = -EILSEQ;
279 break;
280 }
281
282 BT_DBG("type 0x%x len %d", h->type, h->len);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900283
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284 switch (h->type & BNEP_TYPE_MASK) {
285 case BNEP_EXT_CONTROL:
286 bnep_rx_control(s, skb->data, skb->len);
287 break;
288
289 default:
290 /* Unknown extension, skip it. */
291 break;
292 }
293
294 if (!skb_pull(skb, h->len)) {
295 err = -EILSEQ;
296 break;
297 }
298 } while (!err && (h->type & BNEP_EXT_HEADER));
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900299
Linus Torvalds1da177e2005-04-16 15:20:36 -0700300 return err;
301}
302
303static u8 __bnep_rx_hlen[] = {
304 ETH_HLEN, /* BNEP_GENERAL */
305 0, /* BNEP_CONTROL */
306 2, /* BNEP_COMPRESSED */
307 ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
308 ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
309};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
312{
313 struct net_device *dev = s->dev;
314 struct sk_buff *nskb;
315 u8 type;
316
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800317 dev->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
Szymon Janc3aad75a2011-03-21 14:19:58 +0100319 type = *(u8 *) skb->data;
320 skb_pull(skb, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
Szymon Janca3d9bd42011-03-21 14:19:57 +0100322 if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 goto badframe;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325 if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
326 bnep_rx_control(s, skb->data, skb->len);
327 kfree_skb(skb);
328 return 0;
329 }
330
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700331 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332
333 /* Verify and pull out header */
334 if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
335 goto badframe;
336
Al Viro1bc5d442006-11-08 00:27:57 -0800337 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
339 if (type & BNEP_EXT_HEADER) {
340 if (bnep_rx_extension(s, skb) < 0)
341 goto badframe;
342 }
343
344 /* Strip 802.1p header */
345 if (ntohs(s->eh.h_proto) == 0x8100) {
346 if (!skb_pull(skb, 4))
347 goto badframe;
Al Viro1bc5d442006-11-08 00:27:57 -0800348 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700349 }
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900350
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 /* We have to alloc new skb and copy data here :(. Because original skb
352 * may not be modified and because of the alignment requirements. */
353 nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
354 if (!nskb) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800355 dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 kfree_skb(skb);
357 return -ENOMEM;
358 }
359 skb_reserve(nskb, 2);
360
361 /* Decompress header and construct ether frame */
362 switch (type & BNEP_TYPE_MASK) {
363 case BNEP_COMPRESSED:
364 memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
365 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 case BNEP_COMPRESSED_SRC_ONLY:
368 memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700369 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
Al Viro1bc5d442006-11-08 00:27:57 -0800370 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 break;
372
373 case BNEP_COMPRESSED_DST_ONLY:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700374 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100375 ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700376 memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
Szymon Janc3aad75a2011-03-21 14:19:58 +0100377 ETH_ALEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 break;
379
380 case BNEP_GENERAL:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700381 memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100382 ETH_ALEN * 2);
Al Viro1bc5d442006-11-08 00:27:57 -0800383 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 break;
385 }
386
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300387 skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 kfree_skb(skb);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900389
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800390 dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 nskb->ip_summed = CHECKSUM_NONE;
392 nskb->protocol = eth_type_trans(nskb, dev);
393 netif_rx_ni(nskb);
394 return 0;
395
396badframe:
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800397 dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 kfree_skb(skb);
399 return 0;
400}
401
402static u8 __bnep_tx_types[] = {
403 BNEP_GENERAL,
404 BNEP_COMPRESSED_SRC_ONLY,
405 BNEP_COMPRESSED_DST_ONLY,
406 BNEP_COMPRESSED
407};
408
409static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
410{
411 struct ethhdr *eh = (void *) skb->data;
412 struct socket *sock = s->sock;
413 struct kvec iv[3];
414 int len = 0, il = 0;
415 u8 type = 0;
416
417 BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
418
419 if (!skb->dev) {
420 /* Control frame sent by us */
421 goto send;
422 }
423
424 iv[il++] = (struct kvec) { &type, 1 };
425 len++;
426
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200427 if (compress_src && !compare_ether_addr(eh->h_dest, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700428 type |= 0x01;
429
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200430 if (compress_dst && !compare_ether_addr(eh->h_source, s->eh.h_dest))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 type |= 0x02;
432
433 if (type)
434 skb_pull(skb, ETH_ALEN * 2);
435
436 type = __bnep_tx_types[type];
437 switch (type) {
438 case BNEP_COMPRESSED_SRC_ONLY:
439 iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
440 len += ETH_ALEN;
441 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900442
Linus Torvalds1da177e2005-04-16 15:20:36 -0700443 case BNEP_COMPRESSED_DST_ONLY:
444 iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
445 len += ETH_ALEN;
446 break;
447 }
448
449send:
450 iv[il++] = (struct kvec) { skb->data, skb->len };
451 len += skb->len;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900452
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453 /* FIXME: linearize skb */
454 {
455 len = kernel_sendmsg(sock, &s->msg, iv, il, len);
456 }
457 kfree_skb(skb);
458
459 if (len > 0) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800460 s->dev->stats.tx_bytes += len;
461 s->dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 return 0;
463 }
464
465 return len;
466}
467
468static int bnep_session(void *arg)
469{
470 struct bnep_session *s = arg;
471 struct net_device *dev = s->dev;
472 struct sock *sk = s->sock->sk;
473 struct sk_buff *skb;
474 wait_queue_t wait;
475
476 BT_DBG("");
477
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
480 init_waitqueue_entry(&wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000481 add_wait_queue(sk_sleep(sk), &wait);
Peter Hurley38d57552011-07-24 00:11:07 -0400482 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 set_current_state(TASK_INTERRUPTIBLE);
484
Peter Hurley751c10a2011-08-05 10:41:35 -0400485 if (atomic_read(&s->terminate))
Peter Hurley38d57552011-07-24 00:11:07 -0400486 break;
Szymon Janc3aad75a2011-03-21 14:19:58 +0100487 /* RX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488 while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
489 skb_orphan(skb);
Mat Martineau44935722011-07-22 14:53:58 -0700490 if (!skb_linearize(skb))
491 bnep_rx_frame(s, skb);
492 else
493 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 }
495
496 if (sk->sk_state != BT_CONNECTED)
497 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900498
Szymon Janc3aad75a2011-03-21 14:19:58 +0100499 /* TX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 while ((skb = skb_dequeue(&sk->sk_write_queue)))
501 if (bnep_tx_frame(s, skb))
502 break;
503 netif_wake_queue(dev);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900504
Linus Torvalds1da177e2005-04-16 15:20:36 -0700505 schedule();
506 }
Peter Hurley38d57552011-07-24 00:11:07 -0400507 __set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000508 remove_wait_queue(sk_sleep(sk), &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
510 /* Cleanup session */
511 down_write(&bnep_session_sem);
512
513 /* Delete network device */
514 unregister_netdev(dev);
515
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200516 /* Wakeup user-space polling for socket errors */
517 s->sock->sk->sk_err = EUNATCH;
518
Eric Dumazetaa395142010-04-20 13:03:51 +0000519 wake_up_interruptible(sk_sleep(s->sock->sk));
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 /* Release the socket */
522 fput(s->sock->file);
523
524 __bnep_unlink_session(s);
525
526 up_write(&bnep_session_sem);
527 free_netdev(dev);
David Herrmann9b338c32011-11-19 13:23:33 +0100528 module_put_and_exit(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700529 return 0;
530}
531
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200532static struct device *bnep_get_device(struct bnep_session *session)
533{
534 bdaddr_t *src = &bt_sk(session->sock->sk)->src;
535 bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
536 struct hci_dev *hdev;
537 struct hci_conn *conn;
538
539 hdev = hci_get_route(dst, src);
540 if (!hdev)
541 return NULL;
542
543 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200544
545 hci_dev_put(hdev);
546
Marcel Holtmannb2cfcd72006-10-15 17:31:05 +0200547 return conn ? &conn->dev : NULL;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200548}
549
Marcel Holtmann384912e2009-08-31 21:08:19 +0000550static struct device_type bnep_type = {
551 .name = "bluetooth",
552};
553
Linus Torvalds1da177e2005-04-16 15:20:36 -0700554int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
555{
556 struct net_device *dev;
557 struct bnep_session *s, *ss;
558 u8 dst[ETH_ALEN], src[ETH_ALEN];
559 int err;
560
561 BT_DBG("");
562
563 baswap((void *) dst, &bt_sk(sock->sk)->dst);
564 baswap((void *) src, &bt_sk(sock->sk)->src);
565
566 /* session struct allocated as private part of net_device */
567 dev = alloc_netdev(sizeof(struct bnep_session),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100568 (*req->device) ? req->device : "bnep%d",
569 bnep_net_setup);
Tobias Klauser67b52e52006-03-21 23:53:16 -0800570 if (!dev)
571 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 down_write(&bnep_session_sem);
574
575 ss = __bnep_get_session(dst);
576 if (ss && ss->state == BT_CONNECTED) {
577 err = -EEXIST;
578 goto failed;
579 }
580
Wang Chen524ad0a2008-11-12 23:39:10 -0800581 s = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700582
583 /* This is rx header therefore addresses are swapped.
Szymon Janc3aad75a2011-03-21 14:19:58 +0100584 * ie. eh.h_dest is our local address. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700585 memcpy(s->eh.h_dest, &src, ETH_ALEN);
586 memcpy(s->eh.h_source, &dst, ETH_ALEN);
587 memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
588
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200589 s->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 s->sock = sock;
591 s->role = req->role;
592 s->state = BT_CONNECTED;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900593
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594 s->msg.msg_flags = MSG_NOSIGNAL;
595
596#ifdef CONFIG_BT_BNEP_MC_FILTER
597 /* Set default mc filter */
598 set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
599#endif
600
601#ifdef CONFIG_BT_BNEP_PROTO_FILTER
602 /* Set default protocol filter */
603 bnep_set_default_proto_filter(s);
604#endif
605
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200606 SET_NETDEV_DEV(dev, bnep_get_device(s));
Marcel Holtmann384912e2009-08-31 21:08:19 +0000607 SET_NETDEV_DEVTYPE(dev, &bnep_type);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 err = register_netdev(dev);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100610 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612
613 __bnep_link_session(s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900614
David Herrmann9b338c32011-11-19 13:23:33 +0100615 __module_get(THIS_MODULE);
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100616 s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
617 if (IS_ERR(s->task)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 /* Session thread start failed, gotta cleanup. */
David Herrmann9b338c32011-11-19 13:23:33 +0100619 module_put(THIS_MODULE);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 unregister_netdev(dev);
621 __bnep_unlink_session(s);
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100622 err = PTR_ERR(s->task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 goto failed;
624 }
625
626 up_write(&bnep_session_sem);
627 strcpy(req->device, dev->name);
628 return 0;
629
630failed:
631 up_write(&bnep_session_sem);
632 free_netdev(dev);
633 return err;
634}
635
636int bnep_del_connection(struct bnep_conndel_req *req)
637{
638 struct bnep_session *s;
639 int err = 0;
640
641 BT_DBG("");
642
643 down_read(&bnep_session_sem);
644
645 s = __bnep_get_session(req->dst);
Peter Hurley751c10a2011-08-05 10:41:35 -0400646 if (s) {
647 atomic_inc(&s->terminate);
648 wake_up_process(s->task);
649 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700650 err = -ENOENT;
651
652 up_read(&bnep_session_sem);
653 return err;
654}
655
656static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
657{
Vasiliy Kulikov5520d202010-10-30 18:26:21 +0400658 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
660 strcpy(ci->device, s->dev->name);
661 ci->flags = s->flags;
662 ci->state = s->state;
663 ci->role = s->role;
664}
665
666int bnep_get_connlist(struct bnep_connlist_req *req)
667{
668 struct list_head *p;
669 int err = 0, n = 0;
670
671 down_read(&bnep_session_sem);
672
673 list_for_each(p, &bnep_session_list) {
674 struct bnep_session *s;
675 struct bnep_conninfo ci;
676
677 s = list_entry(p, struct bnep_session, list);
678
679 __bnep_copy_ci(&ci, s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900680
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
682 err = -EFAULT;
683 break;
684 }
685
686 if (++n >= req->cnum)
687 break;
688
689 req->ci++;
690 }
691 req->cnum = n;
692
693 up_read(&bnep_session_sem);
694 return err;
695}
696
697int bnep_get_conninfo(struct bnep_conninfo *ci)
698{
699 struct bnep_session *s;
700 int err = 0;
701
702 down_read(&bnep_session_sem);
703
704 s = __bnep_get_session(ci->dst);
705 if (s)
706 __bnep_copy_ci(ci, s);
707 else
708 err = -ENOENT;
709
710 up_read(&bnep_session_sem);
711 return err;
712}
713
714static int __init bnep_init(void)
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900715{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 char flt[50] = "";
717
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718#ifdef CONFIG_BT_BNEP_PROTO_FILTER
719 strcat(flt, "protocol ");
720#endif
721
722#ifdef CONFIG_BT_BNEP_MC_FILTER
723 strcat(flt, "multicast");
724#endif
725
726 BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
727 if (flt[0])
728 BT_INFO("BNEP filters: %s", flt);
729
730 bnep_sock_init();
731 return 0;
732}
733
734static void __exit bnep_exit(void)
735{
736 bnep_sock_cleanup();
737}
738
739module_init(bnep_init);
740module_exit(bnep_exit);
741
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200742module_param(compress_src, bool, 0644);
743MODULE_PARM_DESC(compress_src, "Compress sources headers");
744
745module_param(compress_dst, bool, 0644);
746MODULE_PARM_DESC(compress_dst, "Compress destination headers");
747
Marcel Holtmann63fbd242008-08-18 13:23:53 +0200748MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700749MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
750MODULE_VERSION(VERSION);
751MODULE_LICENSE("GPL");
752MODULE_ALIAS("bt-proto-4");