blob: f504921921972f96b3e86fe6782c59a75c843bbf [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>
Steve Mucklef132c6c2012-06-06 18:30:57 -070029#include <linux/interrupt.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31#include <linux/kernel.h>
32#include <linux/sched.h>
33#include <linux/signal.h>
34#include <linux/init.h>
35#include <linux/wait.h>
Rafael J. Wysocki83144182007-07-17 04:03:35 -070036#include <linux/freezer.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#include <linux/net.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090039#include <linux/slab.h>
Szymon Jancf4d7cd42011-03-21 14:20:00 +010040#include <linux/kthread.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#include <net/sock.h>
42
43#include <linux/socket.h>
44#include <linux/file.h>
45
46#include <linux/netdevice.h>
47#include <linux/etherdevice.h>
48#include <linux/skbuff.h>
49
50#include <asm/unaligned.h>
51
52#include <net/bluetooth/bluetooth.h>
Marcel Holtmann0a85b962006-07-06 13:09:02 +020053#include <net/bluetooth/hci_core.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#include <net/bluetooth/l2cap.h>
55
56#include "bnep.h"
57
Marcel Holtmann28111eb2008-08-07 22:26:54 +020058#define VERSION "1.3"
59
Steve Mucklef132c6c2012-06-06 18:30:57 -070060static bool compress_src = 1;
61static bool compress_dst = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070062
63static LIST_HEAD(bnep_session_list);
64static DECLARE_RWSEM(bnep_session_sem);
65
66static struct bnep_session *__bnep_get_session(u8 *dst)
67{
68 struct bnep_session *s;
69 struct list_head *p;
70
71 BT_DBG("");
72
73 list_for_each(p, &bnep_session_list) {
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090074 s = list_entry(p, struct bnep_session, list);
Kris Katterjohnd3f4a682006-01-09 16:01:43 -080075 if (!compare_ether_addr(dst, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 return s;
77 }
78 return NULL;
79}
80
81static void __bnep_link_session(struct bnep_session *s)
82{
83 /* It's safe to call __module_get() here because sessions are added
Thadeu Lima de Souza Cascardo94e2bd62009-10-16 15:20:49 +020084 by the socket layer which has to hold the reference to this module.
Linus Torvalds1da177e2005-04-16 15:20:36 -070085 */
86 __module_get(THIS_MODULE);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090087 list_add(&s->list, &bnep_session_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070088}
89
90static void __bnep_unlink_session(struct bnep_session *s)
91{
92 list_del(&s->list);
93 module_put(THIS_MODULE);
94}
95
96static int bnep_send(struct bnep_session *s, void *data, size_t len)
97{
98 struct socket *sock = s->sock;
99 struct kvec iv = { data, len };
100
101 return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
102}
103
104static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
105{
106 struct bnep_control_rsp rsp;
107 rsp.type = BNEP_CONTROL;
108 rsp.ctrl = ctrl;
109 rsp.resp = htons(resp);
110 return bnep_send(s, &rsp, sizeof(rsp));
111}
112
113#ifdef CONFIG_BT_BNEP_PROTO_FILTER
114static inline void bnep_set_default_proto_filter(struct bnep_session *s)
115{
116 /* (IPv4, ARP) */
Al Viroe41d2162006-11-08 00:27:36 -0800117 s->proto_filter[0].start = ETH_P_IP;
118 s->proto_filter[0].end = ETH_P_ARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 /* (RARP, AppleTalk) */
Al Viroe41d2162006-11-08 00:27:36 -0800120 s->proto_filter[1].start = ETH_P_RARP;
121 s->proto_filter[1].end = ETH_P_AARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 /* (IPX, IPv6) */
Al Viroe41d2162006-11-08 00:27:36 -0800123 s->proto_filter[2].start = ETH_P_IPX;
124 s->proto_filter[2].end = ETH_P_IPV6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125}
126#endif
127
Al Viro1bc5d442006-11-08 00:27:57 -0800128static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129{
130 int n;
131
132 if (len < 2)
133 return -EILSEQ;
134
Harvey Harrison83985312008-05-02 16:25:46 -0700135 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100136 data++;
137 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139 if (len < n)
140 return -EILSEQ;
141
142 BT_DBG("filter len %d", n);
143
144#ifdef CONFIG_BT_BNEP_PROTO_FILTER
145 n /= 4;
146 if (n <= BNEP_MAX_PROTO_FILTERS) {
147 struct bnep_proto_filter *f = s->proto_filter;
148 int i;
149
150 for (i = 0; i < n; i++) {
Harvey Harrison83985312008-05-02 16:25:46 -0700151 f[i].start = get_unaligned_be16(data++);
152 f[i].end = get_unaligned_be16(data++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153
154 BT_DBG("proto filter start %d end %d",
155 f[i].start, f[i].end);
156 }
157
158 if (i < BNEP_MAX_PROTO_FILTERS)
159 memset(f + i, 0, sizeof(*f));
160
161 if (n == 0)
162 bnep_set_default_proto_filter(s);
163
164 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
165 } else {
166 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
167 }
168#else
169 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
170#endif
171 return 0;
172}
173
174static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
175{
176 int n;
177
178 if (len < 2)
179 return -EILSEQ;
180
Harvey Harrison83985312008-05-02 16:25:46 -0700181 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100182 data += 2;
183 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
185 if (len < n)
186 return -EILSEQ;
187
188 BT_DBG("filter len %d", n);
189
190#ifdef CONFIG_BT_BNEP_MC_FILTER
191 n /= (ETH_ALEN * 2);
192
193 if (n > 0) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100194 int i;
195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 s->mc_filter = 0;
197
198 /* Always send broadcast */
199 set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
200
201 /* Add address ranges to the multicast hash */
202 for (; n > 0; n--) {
203 u8 a1[6], *a2;
204
Szymon Janc3aad75a2011-03-21 14:19:58 +0100205 memcpy(a1, data, ETH_ALEN);
206 data += ETH_ALEN;
207 a2 = data;
208 data += ETH_ALEN;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 BT_DBG("mc filter %s -> %s",
211 batostr((void *) a1), batostr((void *) a2));
212
Linus Torvalds1da177e2005-04-16 15:20:36 -0700213 /* Iterate from a1 to a2 */
214 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
215 while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100216 /* Increment a1 */
217 i = 5;
218 while (i >= 0 && ++a1[i--] == 0)
219 ;
220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
222 }
223 }
224 }
225
226 BT_DBG("mc filter hash 0x%llx", s->mc_filter);
227
228 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
229#else
230 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
231#endif
232 return 0;
233}
234
235static int bnep_rx_control(struct bnep_session *s, void *data, int len)
236{
237 u8 cmd = *(u8 *)data;
238 int err = 0;
239
Szymon Janc3aad75a2011-03-21 14:19:58 +0100240 data++;
241 len--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242
243 switch (cmd) {
244 case BNEP_CMD_NOT_UNDERSTOOD:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 case BNEP_SETUP_CONN_RSP:
246 case BNEP_FILTER_NET_TYPE_RSP:
247 case BNEP_FILTER_MULTI_ADDR_RSP:
248 /* Ignore these for now */
249 break;
250
251 case BNEP_FILTER_NET_TYPE_SET:
252 err = bnep_ctrl_set_netfilter(s, data, len);
253 break;
254
255 case BNEP_FILTER_MULTI_ADDR_SET:
256 err = bnep_ctrl_set_mcfilter(s, data, len);
257 break;
258
Vikram Kandukuricde9f802009-12-03 15:12:51 +0530259 case BNEP_SETUP_CONN_REQ:
260 err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
261 break;
262
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263 default: {
264 u8 pkt[3];
265 pkt[0] = BNEP_CONTROL;
266 pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
267 pkt[2] = cmd;
268 bnep_send(s, pkt, sizeof(pkt));
269 }
270 break;
271 }
272
273 return err;
274}
275
276static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
277{
278 struct bnep_ext_hdr *h;
279 int err = 0;
280
281 do {
282 h = (void *) skb->data;
283 if (!skb_pull(skb, sizeof(*h))) {
284 err = -EILSEQ;
285 break;
286 }
287
288 BT_DBG("type 0x%x len %d", h->type, h->len);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 switch (h->type & BNEP_TYPE_MASK) {
291 case BNEP_EXT_CONTROL:
292 bnep_rx_control(s, skb->data, skb->len);
293 break;
294
295 default:
296 /* Unknown extension, skip it. */
297 break;
298 }
299
300 if (!skb_pull(skb, h->len)) {
301 err = -EILSEQ;
302 break;
303 }
304 } while (!err && (h->type & BNEP_EXT_HEADER));
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900305
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 return err;
307}
308
309static u8 __bnep_rx_hlen[] = {
310 ETH_HLEN, /* BNEP_GENERAL */
311 0, /* BNEP_CONTROL */
312 2, /* BNEP_COMPRESSED */
313 ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
314 ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
315};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700316
317static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
318{
319 struct net_device *dev = s->dev;
320 struct sk_buff *nskb;
321 u8 type;
322
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800323 dev->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324
Szymon Janc3aad75a2011-03-21 14:19:58 +0100325 type = *(u8 *) skb->data;
326 skb_pull(skb, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700327
Szymon Janca3d9bd42011-03-21 14:19:57 +0100328 if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329 goto badframe;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900330
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331 if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
332 bnep_rx_control(s, skb->data, skb->len);
333 kfree_skb(skb);
334 return 0;
335 }
336
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700337 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338
339 /* Verify and pull out header */
340 if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
341 goto badframe;
342
Al Viro1bc5d442006-11-08 00:27:57 -0800343 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
345 if (type & BNEP_EXT_HEADER) {
346 if (bnep_rx_extension(s, skb) < 0)
347 goto badframe;
348 }
349
350 /* Strip 802.1p header */
351 if (ntohs(s->eh.h_proto) == 0x8100) {
352 if (!skb_pull(skb, 4))
353 goto badframe;
Al Viro1bc5d442006-11-08 00:27:57 -0800354 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 }
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 /* We have to alloc new skb and copy data here :(. Because original skb
358 * may not be modified and because of the alignment requirements. */
359 nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
360 if (!nskb) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800361 dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 kfree_skb(skb);
363 return -ENOMEM;
364 }
365 skb_reserve(nskb, 2);
366
367 /* Decompress header and construct ether frame */
368 switch (type & BNEP_TYPE_MASK) {
369 case BNEP_COMPRESSED:
370 memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
371 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900372
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373 case BNEP_COMPRESSED_SRC_ONLY:
374 memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700375 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
Al Viro1bc5d442006-11-08 00:27:57 -0800376 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 break;
378
379 case BNEP_COMPRESSED_DST_ONLY:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700380 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100381 ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700382 memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
Szymon Janc3aad75a2011-03-21 14:19:58 +0100383 ETH_ALEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 break;
385
386 case BNEP_GENERAL:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700387 memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100388 ETH_ALEN * 2);
Al Viro1bc5d442006-11-08 00:27:57 -0800389 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700390 break;
391 }
392
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300393 skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 kfree_skb(skb);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900395
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800396 dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700397 nskb->ip_summed = CHECKSUM_NONE;
398 nskb->protocol = eth_type_trans(nskb, dev);
399 netif_rx_ni(nskb);
400 return 0;
401
402badframe:
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800403 dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 kfree_skb(skb);
405 return 0;
406}
407
408static u8 __bnep_tx_types[] = {
409 BNEP_GENERAL,
410 BNEP_COMPRESSED_SRC_ONLY,
411 BNEP_COMPRESSED_DST_ONLY,
412 BNEP_COMPRESSED
413};
414
415static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
416{
417 struct ethhdr *eh = (void *) skb->data;
418 struct socket *sock = s->sock;
419 struct kvec iv[3];
420 int len = 0, il = 0;
421 u8 type = 0;
422
423 BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
424
425 if (!skb->dev) {
426 /* Control frame sent by us */
427 goto send;
428 }
429
430 iv[il++] = (struct kvec) { &type, 1 };
431 len++;
432
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200433 if (compress_src && !compare_ether_addr(eh->h_dest, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 type |= 0x01;
435
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200436 if (compress_dst && !compare_ether_addr(eh->h_source, s->eh.h_dest))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 type |= 0x02;
438
439 if (type)
440 skb_pull(skb, ETH_ALEN * 2);
441
442 type = __bnep_tx_types[type];
443 switch (type) {
444 case BNEP_COMPRESSED_SRC_ONLY:
445 iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
446 len += ETH_ALEN;
447 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900448
Linus Torvalds1da177e2005-04-16 15:20:36 -0700449 case BNEP_COMPRESSED_DST_ONLY:
450 iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
451 len += ETH_ALEN;
452 break;
453 }
454
455send:
456 iv[il++] = (struct kvec) { skb->data, skb->len };
457 len += skb->len;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900458
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 /* FIXME: linearize skb */
460 {
461 len = kernel_sendmsg(sock, &s->msg, iv, il, len);
462 }
463 kfree_skb(skb);
464
465 if (len > 0) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800466 s->dev->stats.tx_bytes += len;
467 s->dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468 return 0;
469 }
470
471 return len;
472}
473
474static int bnep_session(void *arg)
475{
476 struct bnep_session *s = arg;
477 struct net_device *dev = s->dev;
478 struct sock *sk = s->sock->sk;
479 struct sk_buff *skb;
480 wait_queue_t wait;
481
482 BT_DBG("");
483
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485
486 init_waitqueue_entry(&wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000487 add_wait_queue(sk_sleep(sk), &wait);
Peter Hurley38d57552011-07-24 00:11:07 -0400488 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700489 set_current_state(TASK_INTERRUPTIBLE);
490
Peter Hurley751c10a2011-08-05 10:41:35 -0400491 if (atomic_read(&s->terminate))
Peter Hurley38d57552011-07-24 00:11:07 -0400492 break;
Szymon Janc3aad75a2011-03-21 14:19:58 +0100493 /* RX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
495 skb_orphan(skb);
Mat Martineau44935722011-07-22 14:53:58 -0700496 if (!skb_linearize(skb))
497 bnep_rx_frame(s, skb);
498 else
499 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700500 }
501
502 if (sk->sk_state != BT_CONNECTED)
503 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900504
Szymon Janc3aad75a2011-03-21 14:19:58 +0100505 /* TX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 while ((skb = skb_dequeue(&sk->sk_write_queue)))
507 if (bnep_tx_frame(s, skb))
508 break;
509 netif_wake_queue(dev);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900510
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511 schedule();
512 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700513 set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000514 remove_wait_queue(sk_sleep(sk), &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
516 /* Cleanup session */
517 down_write(&bnep_session_sem);
518
519 /* Delete network device */
520 unregister_netdev(dev);
521
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200522 /* Wakeup user-space polling for socket errors */
523 s->sock->sk->sk_err = EUNATCH;
524
Eric Dumazetaa395142010-04-20 13:03:51 +0000525 wake_up_interruptible(sk_sleep(s->sock->sk));
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200526
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 /* Release the socket */
528 fput(s->sock->file);
529
530 __bnep_unlink_session(s);
531
532 up_write(&bnep_session_sem);
533 free_netdev(dev);
534 return 0;
535}
536
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200537static struct device *bnep_get_device(struct bnep_session *session)
538{
539 bdaddr_t *src = &bt_sk(session->sock->sk)->src;
540 bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
541 struct hci_dev *hdev;
542 struct hci_conn *conn;
543
544 hdev = hci_get_route(dst, src);
545 if (!hdev)
546 return NULL;
547
548 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200549
550 hci_dev_put(hdev);
551
Marcel Holtmannb2cfcd72006-10-15 17:31:05 +0200552 return conn ? &conn->dev : NULL;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200553}
554
Marcel Holtmann384912e2009-08-31 21:08:19 +0000555static struct device_type bnep_type = {
556 .name = "bluetooth",
557};
558
Linus Torvalds1da177e2005-04-16 15:20:36 -0700559int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
560{
561 struct net_device *dev;
562 struct bnep_session *s, *ss;
563 u8 dst[ETH_ALEN], src[ETH_ALEN];
564 int err;
565
566 BT_DBG("");
567
568 baswap((void *) dst, &bt_sk(sock->sk)->dst);
569 baswap((void *) src, &bt_sk(sock->sk)->src);
570
571 /* session struct allocated as private part of net_device */
572 dev = alloc_netdev(sizeof(struct bnep_session),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100573 (*req->device) ? req->device : "bnep%d",
574 bnep_net_setup);
Tobias Klauser67b52e52006-03-21 23:53:16 -0800575 if (!dev)
576 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 down_write(&bnep_session_sem);
579
580 ss = __bnep_get_session(dst);
581 if (ss && ss->state == BT_CONNECTED) {
582 err = -EEXIST;
583 goto failed;
584 }
585
Wang Chen524ad0a2008-11-12 23:39:10 -0800586 s = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
588 /* This is rx header therefore addresses are swapped.
Szymon Janc3aad75a2011-03-21 14:19:58 +0100589 * ie. eh.h_dest is our local address. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 memcpy(s->eh.h_dest, &src, ETH_ALEN);
591 memcpy(s->eh.h_source, &dst, ETH_ALEN);
592 memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
593
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200594 s->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 s->sock = sock;
596 s->role = req->role;
597 s->state = BT_CONNECTED;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900598
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 s->msg.msg_flags = MSG_NOSIGNAL;
600
601#ifdef CONFIG_BT_BNEP_MC_FILTER
602 /* Set default mc filter */
603 set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
604#endif
605
606#ifdef CONFIG_BT_BNEP_PROTO_FILTER
607 /* Set default protocol filter */
608 bnep_set_default_proto_filter(s);
609#endif
610
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200611 SET_NETDEV_DEV(dev, bnep_get_device(s));
Marcel Holtmann384912e2009-08-31 21:08:19 +0000612 SET_NETDEV_DEVTYPE(dev, &bnep_type);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200613
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 err = register_netdev(dev);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100615 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700616 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617
618 __bnep_link_session(s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900619
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100620 s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
621 if (IS_ERR(s->task)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 /* Session thread start failed, gotta cleanup. */
623 unregister_netdev(dev);
624 __bnep_unlink_session(s);
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100625 err = PTR_ERR(s->task);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626 goto failed;
627 }
628
629 up_write(&bnep_session_sem);
630 strcpy(req->device, dev->name);
631 return 0;
632
633failed:
634 up_write(&bnep_session_sem);
635 free_netdev(dev);
636 return err;
637}
638
639int bnep_del_connection(struct bnep_conndel_req *req)
640{
641 struct bnep_session *s;
642 int err = 0;
643
644 BT_DBG("");
645
646 down_read(&bnep_session_sem);
647
648 s = __bnep_get_session(req->dst);
Peter Hurley751c10a2011-08-05 10:41:35 -0400649 if (s) {
650 atomic_inc(&s->terminate);
651 wake_up_process(s->task);
652 } else
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 err = -ENOENT;
654
655 up_read(&bnep_session_sem);
656 return err;
657}
658
659static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
660{
Vasiliy Kulikov5520d202010-10-30 18:26:21 +0400661 memset(ci, 0, sizeof(*ci));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
663 strcpy(ci->device, s->dev->name);
664 ci->flags = s->flags;
665 ci->state = s->state;
666 ci->role = s->role;
667}
668
669int bnep_get_connlist(struct bnep_connlist_req *req)
670{
671 struct list_head *p;
672 int err = 0, n = 0;
673
674 down_read(&bnep_session_sem);
675
676 list_for_each(p, &bnep_session_list) {
677 struct bnep_session *s;
678 struct bnep_conninfo ci;
679
680 s = list_entry(p, struct bnep_session, list);
681
682 __bnep_copy_ci(&ci, s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900683
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
685 err = -EFAULT;
686 break;
687 }
688
689 if (++n >= req->cnum)
690 break;
691
692 req->ci++;
693 }
694 req->cnum = n;
695
696 up_read(&bnep_session_sem);
697 return err;
698}
699
700int bnep_get_conninfo(struct bnep_conninfo *ci)
701{
702 struct bnep_session *s;
703 int err = 0;
704
705 down_read(&bnep_session_sem);
706
707 s = __bnep_get_session(ci->dst);
708 if (s)
709 __bnep_copy_ci(ci, s);
710 else
711 err = -ENOENT;
712
713 up_read(&bnep_session_sem);
714 return err;
715}
716
717static int __init bnep_init(void)
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900718{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700719 char flt[50] = "";
720
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721#ifdef CONFIG_BT_BNEP_PROTO_FILTER
722 strcat(flt, "protocol ");
723#endif
724
725#ifdef CONFIG_BT_BNEP_MC_FILTER
726 strcat(flt, "multicast");
727#endif
728
729 BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
730 if (flt[0])
731 BT_INFO("BNEP filters: %s", flt);
732
733 bnep_sock_init();
734 return 0;
735}
736
737static void __exit bnep_exit(void)
738{
739 bnep_sock_cleanup();
740}
741
742module_init(bnep_init);
743module_exit(bnep_exit);
744
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200745module_param(compress_src, bool, 0644);
746MODULE_PARM_DESC(compress_src, "Compress sources headers");
747
748module_param(compress_dst, bool, 0644);
749MODULE_PARM_DESC(compress_dst, "Compress destination headers");
750
Marcel Holtmann63fbd242008-08-18 13:23:53 +0200751MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700752MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
753MODULE_VERSION(VERSION);
754MODULE_LICENSE("GPL");
755MODULE_ALIAS("bt-proto-4");