blob: a6cd856046ab247cb29da2d9831077153da8a642 [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;
Linus Torvalds1da177e2005-04-16 15:20:36 -070068
69 BT_DBG("");
70
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +020071 list_for_each_entry(s, &bnep_session_list, list)
Kris Katterjohnd3f4a682006-01-09 16:01:43 -080072 if (!compare_ether_addr(dst, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -070073 return s;
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +020074
Linus Torvalds1da177e2005-04-16 15:20:36 -070075 return NULL;
76}
77
78static void __bnep_link_session(struct bnep_session *s)
79{
80 /* It's safe to call __module_get() here because sessions are added
Thadeu Lima de Souza Cascardo94e2bd62009-10-16 15:20:49 +020081 by the socket layer which has to hold the reference to this module.
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 */
83 __module_get(THIS_MODULE);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +090084 list_add(&s->list, &bnep_session_list);
Linus Torvalds1da177e2005-04-16 15:20:36 -070085}
86
87static void __bnep_unlink_session(struct bnep_session *s)
88{
89 list_del(&s->list);
90 module_put(THIS_MODULE);
91}
92
93static int bnep_send(struct bnep_session *s, void *data, size_t len)
94{
95 struct socket *sock = s->sock;
96 struct kvec iv = { data, len };
97
98 return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
99}
100
101static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
102{
103 struct bnep_control_rsp rsp;
104 rsp.type = BNEP_CONTROL;
105 rsp.ctrl = ctrl;
106 rsp.resp = htons(resp);
107 return bnep_send(s, &rsp, sizeof(rsp));
108}
109
110#ifdef CONFIG_BT_BNEP_PROTO_FILTER
111static inline void bnep_set_default_proto_filter(struct bnep_session *s)
112{
113 /* (IPv4, ARP) */
Al Viroe41d2162006-11-08 00:27:36 -0800114 s->proto_filter[0].start = ETH_P_IP;
115 s->proto_filter[0].end = ETH_P_ARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 /* (RARP, AppleTalk) */
Al Viroe41d2162006-11-08 00:27:36 -0800117 s->proto_filter[1].start = ETH_P_RARP;
118 s->proto_filter[1].end = ETH_P_AARP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 /* (IPX, IPv6) */
Al Viroe41d2162006-11-08 00:27:36 -0800120 s->proto_filter[2].start = ETH_P_IPX;
121 s->proto_filter[2].end = ETH_P_IPV6;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122}
123#endif
124
Al Viro1bc5d442006-11-08 00:27:57 -0800125static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
127 int n;
128
129 if (len < 2)
130 return -EILSEQ;
131
Harvey Harrison83985312008-05-02 16:25:46 -0700132 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100133 data++;
134 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
136 if (len < n)
137 return -EILSEQ;
138
139 BT_DBG("filter len %d", n);
140
141#ifdef CONFIG_BT_BNEP_PROTO_FILTER
142 n /= 4;
143 if (n <= BNEP_MAX_PROTO_FILTERS) {
144 struct bnep_proto_filter *f = s->proto_filter;
145 int i;
146
147 for (i = 0; i < n; i++) {
Harvey Harrison83985312008-05-02 16:25:46 -0700148 f[i].start = get_unaligned_be16(data++);
149 f[i].end = get_unaligned_be16(data++);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150
151 BT_DBG("proto filter start %d end %d",
152 f[i].start, f[i].end);
153 }
154
155 if (i < BNEP_MAX_PROTO_FILTERS)
156 memset(f + i, 0, sizeof(*f));
157
158 if (n == 0)
159 bnep_set_default_proto_filter(s);
160
161 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
162 } else {
163 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
164 }
165#else
166 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
167#endif
168 return 0;
169}
170
171static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
172{
173 int n;
174
175 if (len < 2)
176 return -EILSEQ;
177
Harvey Harrison83985312008-05-02 16:25:46 -0700178 n = get_unaligned_be16(data);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100179 data += 2;
180 len -= 2;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181
182 if (len < n)
183 return -EILSEQ;
184
185 BT_DBG("filter len %d", n);
186
187#ifdef CONFIG_BT_BNEP_MC_FILTER
188 n /= (ETH_ALEN * 2);
189
190 if (n > 0) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100191 int i;
192
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193 s->mc_filter = 0;
194
195 /* Always send broadcast */
196 set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
197
198 /* Add address ranges to the multicast hash */
199 for (; n > 0; n--) {
200 u8 a1[6], *a2;
201
Szymon Janc3aad75a2011-03-21 14:19:58 +0100202 memcpy(a1, data, ETH_ALEN);
203 data += ETH_ALEN;
204 a2 = data;
205 data += ETH_ALEN;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900206
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 BT_DBG("mc filter %s -> %s",
208 batostr((void *) a1), batostr((void *) a2));
209
Linus Torvalds1da177e2005-04-16 15:20:36 -0700210 /* Iterate from a1 to a2 */
211 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
212 while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
Szymon Janca3d9bd42011-03-21 14:19:57 +0100213 /* Increment a1 */
214 i = 5;
215 while (i >= 0 && ++a1[i--] == 0)
216 ;
217
Linus Torvalds1da177e2005-04-16 15:20:36 -0700218 set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
219 }
220 }
221 }
222
223 BT_DBG("mc filter hash 0x%llx", s->mc_filter);
224
225 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
226#else
227 bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
228#endif
229 return 0;
230}
231
232static int bnep_rx_control(struct bnep_session *s, void *data, int len)
233{
234 u8 cmd = *(u8 *)data;
235 int err = 0;
236
Szymon Janc3aad75a2011-03-21 14:19:58 +0100237 data++;
238 len--;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
240 switch (cmd) {
241 case BNEP_CMD_NOT_UNDERSTOOD:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700242 case BNEP_SETUP_CONN_RSP:
243 case BNEP_FILTER_NET_TYPE_RSP:
244 case BNEP_FILTER_MULTI_ADDR_RSP:
245 /* Ignore these for now */
246 break;
247
248 case BNEP_FILTER_NET_TYPE_SET:
249 err = bnep_ctrl_set_netfilter(s, data, len);
250 break;
251
252 case BNEP_FILTER_MULTI_ADDR_SET:
253 err = bnep_ctrl_set_mcfilter(s, data, len);
254 break;
255
Vikram Kandukuricde9f802009-12-03 15:12:51 +0530256 case BNEP_SETUP_CONN_REQ:
257 err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
258 break;
259
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260 default: {
261 u8 pkt[3];
262 pkt[0] = BNEP_CONTROL;
263 pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
264 pkt[2] = cmd;
265 bnep_send(s, pkt, sizeof(pkt));
266 }
267 break;
268 }
269
270 return err;
271}
272
273static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
274{
275 struct bnep_ext_hdr *h;
276 int err = 0;
277
278 do {
279 h = (void *) skb->data;
280 if (!skb_pull(skb, sizeof(*h))) {
281 err = -EILSEQ;
282 break;
283 }
284
285 BT_DBG("type 0x%x len %d", h->type, h->len);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900286
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 switch (h->type & BNEP_TYPE_MASK) {
288 case BNEP_EXT_CONTROL:
289 bnep_rx_control(s, skb->data, skb->len);
290 break;
291
292 default:
293 /* Unknown extension, skip it. */
294 break;
295 }
296
297 if (!skb_pull(skb, h->len)) {
298 err = -EILSEQ;
299 break;
300 }
301 } while (!err && (h->type & BNEP_EXT_HEADER));
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900302
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303 return err;
304}
305
306static u8 __bnep_rx_hlen[] = {
307 ETH_HLEN, /* BNEP_GENERAL */
308 0, /* BNEP_CONTROL */
309 2, /* BNEP_COMPRESSED */
310 ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
311 ETH_ALEN + 2 /* BNEP_COMPRESSED_DST_ONLY */
312};
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313
314static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
315{
316 struct net_device *dev = s->dev;
317 struct sk_buff *nskb;
318 u8 type;
319
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800320 dev->stats.rx_bytes += skb->len;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700321
Szymon Janc3aad75a2011-03-21 14:19:58 +0100322 type = *(u8 *) skb->data;
323 skb_pull(skb, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324
Szymon Janca3d9bd42011-03-21 14:19:57 +0100325 if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 goto badframe;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900327
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
329 bnep_rx_control(s, skb->data, skb->len);
330 kfree_skb(skb);
331 return 0;
332 }
333
Arnaldo Carvalho de Melo459a98e2007-03-19 15:30:44 -0700334 skb_reset_mac_header(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
336 /* Verify and pull out header */
337 if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
338 goto badframe;
339
Al Viro1bc5d442006-11-08 00:27:57 -0800340 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700341
342 if (type & BNEP_EXT_HEADER) {
343 if (bnep_rx_extension(s, skb) < 0)
344 goto badframe;
345 }
346
347 /* Strip 802.1p header */
348 if (ntohs(s->eh.h_proto) == 0x8100) {
349 if (!skb_pull(skb, 4))
350 goto badframe;
Al Viro1bc5d442006-11-08 00:27:57 -0800351 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 }
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900353
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354 /* We have to alloc new skb and copy data here :(. Because original skb
355 * may not be modified and because of the alignment requirements. */
356 nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
357 if (!nskb) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800358 dev->stats.rx_dropped++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359 kfree_skb(skb);
360 return -ENOMEM;
361 }
362 skb_reserve(nskb, 2);
363
364 /* Decompress header and construct ether frame */
365 switch (type & BNEP_TYPE_MASK) {
366 case BNEP_COMPRESSED:
367 memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
368 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900369
Linus Torvalds1da177e2005-04-16 15:20:36 -0700370 case BNEP_COMPRESSED_SRC_ONLY:
371 memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700372 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
Al Viro1bc5d442006-11-08 00:27:57 -0800373 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374 break;
375
376 case BNEP_COMPRESSED_DST_ONLY:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700377 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100378 ETH_ALEN);
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700379 memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
Szymon Janc3aad75a2011-03-21 14:19:58 +0100380 ETH_ALEN + 2);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700381 break;
382
383 case BNEP_GENERAL:
Arnaldo Carvalho de Melo98e399f2007-03-19 15:33:04 -0700384 memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100385 ETH_ALEN * 2);
Al Viro1bc5d442006-11-08 00:27:57 -0800386 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387 break;
388 }
389
Arnaldo Carvalho de Melod626f622007-03-27 18:55:52 -0300390 skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700391 kfree_skb(skb);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900392
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800393 dev->stats.rx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 nskb->ip_summed = CHECKSUM_NONE;
395 nskb->protocol = eth_type_trans(nskb, dev);
396 netif_rx_ni(nskb);
397 return 0;
398
399badframe:
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800400 dev->stats.rx_errors++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 kfree_skb(skb);
402 return 0;
403}
404
405static u8 __bnep_tx_types[] = {
406 BNEP_GENERAL,
407 BNEP_COMPRESSED_SRC_ONLY,
408 BNEP_COMPRESSED_DST_ONLY,
409 BNEP_COMPRESSED
410};
411
412static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
413{
414 struct ethhdr *eh = (void *) skb->data;
415 struct socket *sock = s->sock;
416 struct kvec iv[3];
417 int len = 0, il = 0;
418 u8 type = 0;
419
420 BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
421
422 if (!skb->dev) {
423 /* Control frame sent by us */
424 goto send;
425 }
426
427 iv[il++] = (struct kvec) { &type, 1 };
428 len++;
429
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200430 if (compress_src && !compare_ether_addr(eh->h_dest, s->eh.h_source))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700431 type |= 0x01;
432
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200433 if (compress_dst && !compare_ether_addr(eh->h_source, s->eh.h_dest))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 type |= 0x02;
435
436 if (type)
437 skb_pull(skb, ETH_ALEN * 2);
438
439 type = __bnep_tx_types[type];
440 switch (type) {
441 case BNEP_COMPRESSED_SRC_ONLY:
442 iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
443 len += ETH_ALEN;
444 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900445
Linus Torvalds1da177e2005-04-16 15:20:36 -0700446 case BNEP_COMPRESSED_DST_ONLY:
447 iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
448 len += ETH_ALEN;
449 break;
450 }
451
452send:
453 iv[il++] = (struct kvec) { skb->data, skb->len };
454 len += skb->len;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900455
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456 /* FIXME: linearize skb */
457 {
458 len = kernel_sendmsg(sock, &s->msg, iv, il, len);
459 }
460 kfree_skb(skb);
461
462 if (len > 0) {
Stephen Hemmingerb4d7f0a2009-01-07 17:23:17 -0800463 s->dev->stats.tx_bytes += len;
464 s->dev->stats.tx_packets++;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465 return 0;
466 }
467
468 return len;
469}
470
471static int bnep_session(void *arg)
472{
473 struct bnep_session *s = arg;
474 struct net_device *dev = s->dev;
475 struct sock *sk = s->sock->sk;
476 struct sk_buff *skb;
477 wait_queue_t wait;
478
479 BT_DBG("");
480
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 set_user_nice(current, -15);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
483 init_waitqueue_entry(&wait, current);
Eric Dumazetaa395142010-04-20 13:03:51 +0000484 add_wait_queue(sk_sleep(sk), &wait);
Peter Hurley38d57552011-07-24 00:11:07 -0400485 while (1) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700486 set_current_state(TASK_INTERRUPTIBLE);
487
Peter Hurley751c10a2011-08-05 10:41:35 -0400488 if (atomic_read(&s->terminate))
Peter Hurley38d57552011-07-24 00:11:07 -0400489 break;
Szymon Janc3aad75a2011-03-21 14:19:58 +0100490 /* RX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
492 skb_orphan(skb);
Mat Martineau44935722011-07-22 14:53:58 -0700493 if (!skb_linearize(skb))
494 bnep_rx_frame(s, skb);
495 else
496 kfree_skb(skb);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497 }
498
499 if (sk->sk_state != BT_CONNECTED)
500 break;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900501
Szymon Janc3aad75a2011-03-21 14:19:58 +0100502 /* TX */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 while ((skb = skb_dequeue(&sk->sk_write_queue)))
504 if (bnep_tx_frame(s, skb))
505 break;
506 netif_wake_queue(dev);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900507
Linus Torvalds1da177e2005-04-16 15:20:36 -0700508 schedule();
509 }
Peter Hurley38d57552011-07-24 00:11:07 -0400510 __set_current_state(TASK_RUNNING);
Eric Dumazetaa395142010-04-20 13:03:51 +0000511 remove_wait_queue(sk_sleep(sk), &wait);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700512
513 /* Cleanup session */
514 down_write(&bnep_session_sem);
515
516 /* Delete network device */
517 unregister_netdev(dev);
518
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200519 /* Wakeup user-space polling for socket errors */
520 s->sock->sk->sk_err = EUNATCH;
521
Eric Dumazetaa395142010-04-20 13:03:51 +0000522 wake_up_interruptible(sk_sleep(s->sock->sk));
Marcel Holtmannec8dab32008-07-14 20:13:53 +0200523
Linus Torvalds1da177e2005-04-16 15:20:36 -0700524 /* Release the socket */
525 fput(s->sock->file);
526
527 __bnep_unlink_session(s);
528
529 up_write(&bnep_session_sem);
530 free_netdev(dev);
531 return 0;
532}
533
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200534static struct device *bnep_get_device(struct bnep_session *session)
535{
536 bdaddr_t *src = &bt_sk(session->sock->sk)->src;
537 bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
538 struct hci_dev *hdev;
539 struct hci_conn *conn;
540
541 hdev = hci_get_route(dst, src);
542 if (!hdev)
543 return NULL;
544
545 conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200546
547 hci_dev_put(hdev);
548
Marcel Holtmannb2cfcd72006-10-15 17:31:05 +0200549 return conn ? &conn->dev : NULL;
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200550}
551
Marcel Holtmann384912e2009-08-31 21:08:19 +0000552static struct device_type bnep_type = {
553 .name = "bluetooth",
554};
555
Linus Torvalds1da177e2005-04-16 15:20:36 -0700556int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
557{
558 struct net_device *dev;
559 struct bnep_session *s, *ss;
560 u8 dst[ETH_ALEN], src[ETH_ALEN];
561 int err;
562
563 BT_DBG("");
564
565 baswap((void *) dst, &bt_sk(sock->sk)->dst);
566 baswap((void *) src, &bt_sk(sock->sk)->src);
567
568 /* session struct allocated as private part of net_device */
569 dev = alloc_netdev(sizeof(struct bnep_session),
Szymon Janc3aad75a2011-03-21 14:19:58 +0100570 (*req->device) ? req->device : "bnep%d",
571 bnep_net_setup);
Tobias Klauser67b52e52006-03-21 23:53:16 -0800572 if (!dev)
573 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700574
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 down_write(&bnep_session_sem);
576
577 ss = __bnep_get_session(dst);
578 if (ss && ss->state == BT_CONNECTED) {
579 err = -EEXIST;
580 goto failed;
581 }
582
Wang Chen524ad0a2008-11-12 23:39:10 -0800583 s = netdev_priv(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584
585 /* This is rx header therefore addresses are swapped.
Szymon Janc3aad75a2011-03-21 14:19:58 +0100586 * ie. eh.h_dest is our local address. */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587 memcpy(s->eh.h_dest, &src, ETH_ALEN);
588 memcpy(s->eh.h_source, &dst, ETH_ALEN);
589 memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
590
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200591 s->dev = dev;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 s->sock = sock;
593 s->role = req->role;
594 s->state = BT_CONNECTED;
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900595
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 s->msg.msg_flags = MSG_NOSIGNAL;
597
598#ifdef CONFIG_BT_BNEP_MC_FILTER
599 /* Set default mc filter */
600 set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
601#endif
602
603#ifdef CONFIG_BT_BNEP_PROTO_FILTER
604 /* Set default protocol filter */
605 bnep_set_default_proto_filter(s);
606#endif
607
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200608 SET_NETDEV_DEV(dev, bnep_get_device(s));
Marcel Holtmann384912e2009-08-31 21:08:19 +0000609 SET_NETDEV_DEVTYPE(dev, &bnep_type);
Marcel Holtmann0a85b962006-07-06 13:09:02 +0200610
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611 err = register_netdev(dev);
Szymon Janc3aad75a2011-03-21 14:19:58 +0100612 if (err)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700613 goto failed;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614
615 __bnep_link_session(s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900616
Szymon Jancf4d7cd42011-03-21 14:20:00 +0100617 s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
618 if (IS_ERR(s->task)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700619 /* Session thread start failed, gotta cleanup. */
620 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{
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200668 struct bnep_session *s;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700669 int err = 0, n = 0;
670
671 down_read(&bnep_session_sem);
672
Luiz Augusto von Dentz8035ded2011-11-01 10:58:56 +0200673 list_for_each_entry(s, &bnep_session_list, list) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 struct bnep_conninfo ci;
675
Linus Torvalds1da177e2005-04-16 15:20:36 -0700676 __bnep_copy_ci(&ci, s);
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900677
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
679 err = -EFAULT;
680 break;
681 }
682
683 if (++n >= req->cnum)
684 break;
685
686 req->ci++;
687 }
688 req->cnum = n;
689
690 up_read(&bnep_session_sem);
691 return err;
692}
693
694int bnep_get_conninfo(struct bnep_conninfo *ci)
695{
696 struct bnep_session *s;
697 int err = 0;
698
699 down_read(&bnep_session_sem);
700
701 s = __bnep_get_session(ci->dst);
702 if (s)
703 __bnep_copy_ci(ci, s);
704 else
705 err = -ENOENT;
706
707 up_read(&bnep_session_sem);
708 return err;
709}
710
711static int __init bnep_init(void)
YOSHIFUJI Hideaki8e87d142007-02-09 23:24:33 +0900712{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 char flt[50] = "";
714
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715#ifdef CONFIG_BT_BNEP_PROTO_FILTER
716 strcat(flt, "protocol ");
717#endif
718
719#ifdef CONFIG_BT_BNEP_MC_FILTER
720 strcat(flt, "multicast");
721#endif
722
723 BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
724 if (flt[0])
725 BT_INFO("BNEP filters: %s", flt);
726
727 bnep_sock_init();
728 return 0;
729}
730
731static void __exit bnep_exit(void)
732{
733 bnep_sock_cleanup();
734}
735
736module_init(bnep_init);
737module_exit(bnep_exit);
738
Marcel Holtmann28111eb2008-08-07 22:26:54 +0200739module_param(compress_src, bool, 0644);
740MODULE_PARM_DESC(compress_src, "Compress sources headers");
741
742module_param(compress_dst, bool, 0644);
743MODULE_PARM_DESC(compress_dst, "Compress destination headers");
744
Marcel Holtmann63fbd242008-08-18 13:23:53 +0200745MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700746MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
747MODULE_VERSION(VERSION);
748MODULE_LICENSE("GPL");
749MODULE_ALIAS("bt-proto-4");