]> err.no Git - linux-2.6/blob - net/bluetooth/bnep/core.c
Merge git://git.infradead.org/mtd-2.6
[linux-2.6] / net / bluetooth / bnep / core.c
1 /*
2    BNEP implementation for Linux Bluetooth stack (BlueZ).
3    Copyright (C) 2001-2002 Inventel Systemes
4    Written 2001-2002 by
5         ClĂ©ment Moreau <clement.moreau@inventel.fr>
6         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
18    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
21    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22
23    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
24    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
25    SOFTWARE IS DISCLAIMED.
26 */
27
28 /*
29  * $Id: core.c,v 1.20 2002/08/04 21:23:58 maxk Exp $
30  */
31
32 #include <linux/module.h>
33
34 #include <linux/kernel.h>
35 #include <linux/sched.h>
36 #include <linux/signal.h>
37 #include <linux/init.h>
38 #include <linux/wait.h>
39 #include <linux/errno.h>
40 #include <linux/net.h>
41 #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>
53 #include <net/bluetooth/hci_core.h>
54 #include <net/bluetooth/l2cap.h>
55
56 #include "bnep.h"
57
58 #ifndef CONFIG_BT_BNEP_DEBUG
59 #undef  BT_DBG
60 #define BT_DBG(D...)
61 #endif
62
63 #define VERSION "1.2"
64
65 static LIST_HEAD(bnep_session_list);
66 static DECLARE_RWSEM(bnep_session_sem);
67
68 static struct bnep_session *__bnep_get_session(u8 *dst)
69 {
70         struct bnep_session *s;
71         struct list_head *p;
72
73         BT_DBG("");
74
75         list_for_each(p, &bnep_session_list) {
76                 s = list_entry(p, struct bnep_session, list);
77                 if (!compare_ether_addr(dst, s->eh.h_source))
78                         return s;
79         }
80         return NULL;
81 }
82
83 static void __bnep_link_session(struct bnep_session *s)
84 {
85         /* It's safe to call __module_get() here because sessions are added
86            by the socket layer which has to hold the refference to this module.
87          */
88         __module_get(THIS_MODULE);
89         list_add(&s->list, &bnep_session_list);
90 }
91
92 static void __bnep_unlink_session(struct bnep_session *s)
93 {
94         list_del(&s->list);
95         module_put(THIS_MODULE);
96 }
97
98 static int bnep_send(struct bnep_session *s, void *data, size_t len)
99 {
100         struct socket *sock = s->sock;
101         struct kvec iv = { data, len };
102
103         return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
104 }
105
106 static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
107 {
108         struct bnep_control_rsp rsp;
109         rsp.type = BNEP_CONTROL;
110         rsp.ctrl = ctrl;
111         rsp.resp = htons(resp);
112         return bnep_send(s, &rsp, sizeof(rsp));
113 }
114
115 #ifdef CONFIG_BT_BNEP_PROTO_FILTER
116 static inline void bnep_set_default_proto_filter(struct bnep_session *s)
117 {
118         /* (IPv4, ARP)  */
119         s->proto_filter[0].start = ETH_P_IP;
120         s->proto_filter[0].end   = ETH_P_ARP;
121         /* (RARP, AppleTalk) */
122         s->proto_filter[1].start = ETH_P_RARP;
123         s->proto_filter[1].end   = ETH_P_AARP;
124         /* (IPX, IPv6) */
125         s->proto_filter[2].start = ETH_P_IPX;
126         s->proto_filter[2].end   = ETH_P_IPV6;
127 }
128 #endif
129
130 static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
131 {
132         int n;
133
134         if (len < 2)
135                 return -EILSEQ;
136
137         n = ntohs(get_unaligned(data));
138         data++; len -= 2;
139
140         if (len < n)
141                 return -EILSEQ;
142
143         BT_DBG("filter len %d", n);
144
145 #ifdef CONFIG_BT_BNEP_PROTO_FILTER
146         n /= 4;
147         if (n <= BNEP_MAX_PROTO_FILTERS) {
148                 struct bnep_proto_filter *f = s->proto_filter;
149                 int i;
150
151                 for (i = 0; i < n; i++) {
152                         f[i].start = ntohs(get_unaligned(data++));
153                         f[i].end   = ntohs(get_unaligned(data++));
154
155                         BT_DBG("proto filter start %d end %d",
156                                 f[i].start, f[i].end);
157                 }
158
159                 if (i < BNEP_MAX_PROTO_FILTERS)
160                         memset(f + i, 0, sizeof(*f));
161
162                 if (n == 0)
163                         bnep_set_default_proto_filter(s);
164
165                 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
166         } else {
167                 bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
168         }
169 #else
170         bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
171 #endif
172         return 0;
173 }
174
175 static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
176 {
177         int n;
178
179         if (len < 2)
180                 return -EILSEQ;
181
182         n = ntohs(get_unaligned((__be16 *) data));
183         data += 2; len -= 2;
184
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) {
194                 s->mc_filter = 0;
195
196                 /* Always send broadcast */
197                 set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
198
199                 /* Add address ranges to the multicast hash */
200                 for (; n > 0; n--) {
201                         u8 a1[6], *a2;
202
203                         memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
204                         a2 = data; data += ETH_ALEN;
205
206                         BT_DBG("mc filter %s -> %s",
207                                 batostr((void *) a1), batostr((void *) a2));
208
209                         #define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
210
211                         /* Iterate from a1 to a2 */
212                         set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
213                         while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
214                                 INCA(a1);
215                                 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
229 static int bnep_rx_control(struct bnep_session *s, void *data, int len)
230 {
231         u8  cmd = *(u8 *)data;
232         int err = 0;
233
234         data++; len--;
235
236         switch (cmd) {
237         case BNEP_CMD_NOT_UNDERSTOOD:
238         case BNEP_SETUP_CONN_REQ:
239         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
253         default: {
254                         u8 pkt[3];
255                         pkt[0] = BNEP_CONTROL;
256                         pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
257                         pkt[2] = cmd;
258                         bnep_send(s, pkt, sizeof(pkt));
259                 }
260                 break;
261         }
262
263         return err;
264 }
265
266 static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
267 {
268         struct bnep_ext_hdr *h;
269         int err = 0;
270
271         do {
272                 h = (void *) skb->data;
273                 if (!skb_pull(skb, sizeof(*h))) {
274                         err = -EILSEQ;
275                         break;
276                 }
277
278                 BT_DBG("type 0x%x len %d", h->type, h->len);
279
280                 switch (h->type & BNEP_TYPE_MASK) {
281                 case BNEP_EXT_CONTROL:
282                         bnep_rx_control(s, skb->data, skb->len);
283                         break;
284
285                 default:
286                         /* Unknown extension, skip it. */
287                         break;
288                 }
289
290                 if (!skb_pull(skb, h->len)) {
291                         err = -EILSEQ;
292                         break;
293                 }
294         } while (!err && (h->type & BNEP_EXT_HEADER));
295
296         return err;
297 }
298
299 static u8 __bnep_rx_hlen[] = {
300         ETH_HLEN,     /* BNEP_GENERAL */
301         0,            /* BNEP_CONTROL */
302         2,            /* BNEP_COMPRESSED */
303         ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
304         ETH_ALEN + 2  /* BNEP_COMPRESSED_DST_ONLY */
305 };
306 #define BNEP_RX_TYPES   (sizeof(__bnep_rx_hlen) - 1)
307
308 static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
309 {
310         struct net_device *dev = s->dev;
311         struct sk_buff *nskb;
312         u8 type;
313
314         dev->last_rx = jiffies;
315         s->stats.rx_bytes += skb->len;
316
317         type = *(u8 *) skb->data; skb_pull(skb, 1);
318
319         if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
320                 goto badframe;
321
322         if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
323                 bnep_rx_control(s, skb->data, skb->len);
324                 kfree_skb(skb);
325                 return 0;
326         }
327
328         skb_reset_mac_header(skb);
329
330         /* Verify and pull out header */
331         if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
332                 goto badframe;
333
334         s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
335
336         if (type & BNEP_EXT_HEADER) {
337                 if (bnep_rx_extension(s, skb) < 0)
338                         goto badframe;
339         }
340
341         /* Strip 802.1p header */
342         if (ntohs(s->eh.h_proto) == 0x8100) {
343                 if (!skb_pull(skb, 4))
344                         goto badframe;
345                 s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
346         }
347
348         /* We have to alloc new skb and copy data here :(. Because original skb
349          * may not be modified and because of the alignment requirements. */
350         nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
351         if (!nskb) {
352                 s->stats.rx_dropped++;
353                 kfree_skb(skb);
354                 return -ENOMEM;
355         }
356         skb_reserve(nskb, 2);
357
358         /* Decompress header and construct ether frame */
359         switch (type & BNEP_TYPE_MASK) {
360         case BNEP_COMPRESSED:
361                 memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
362                 break;
363
364         case BNEP_COMPRESSED_SRC_ONLY:
365                 memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
366                 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
367                 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
368                 break;
369
370         case BNEP_COMPRESSED_DST_ONLY:
371                 memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
372                        ETH_ALEN);
373                 memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
374                        ETH_ALEN + 2);
375                 break;
376
377         case BNEP_GENERAL:
378                 memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
379                        ETH_ALEN * 2);
380                 put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
381                 break;
382         }
383
384         skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
385         kfree_skb(skb);
386
387         s->stats.rx_packets++;
388         nskb->ip_summed = CHECKSUM_NONE;
389         nskb->protocol  = eth_type_trans(nskb, dev);
390         netif_rx_ni(nskb);
391         return 0;
392
393 badframe:
394         s->stats.rx_errors++;
395         kfree_skb(skb);
396         return 0;
397 }
398
399 static u8 __bnep_tx_types[] = {
400         BNEP_GENERAL,
401         BNEP_COMPRESSED_SRC_ONLY,
402         BNEP_COMPRESSED_DST_ONLY,
403         BNEP_COMPRESSED
404 };
405
406 static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
407 {
408         struct ethhdr *eh = (void *) skb->data;
409         struct socket *sock = s->sock;
410         struct kvec iv[3];
411         int len = 0, il = 0;
412         u8 type = 0;
413
414         BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
415
416         if (!skb->dev) {
417                 /* Control frame sent by us */
418                 goto send;
419         }
420
421         iv[il++] = (struct kvec) { &type, 1 };
422         len++;
423
424         if (!compare_ether_addr(eh->h_dest, s->eh.h_source))
425                 type |= 0x01;
426
427         if (!compare_ether_addr(eh->h_source, s->eh.h_dest))
428                 type |= 0x02;
429
430         if (type)
431                 skb_pull(skb, ETH_ALEN * 2);
432
433         type = __bnep_tx_types[type];
434         switch (type) {
435         case BNEP_COMPRESSED_SRC_ONLY:
436                 iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
437                 len += ETH_ALEN;
438                 break;
439
440         case BNEP_COMPRESSED_DST_ONLY:
441                 iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
442                 len += ETH_ALEN;
443                 break;
444         }
445
446 send:
447         iv[il++] = (struct kvec) { skb->data, skb->len };
448         len += skb->len;
449
450         /* FIXME: linearize skb */
451         {
452                 len = kernel_sendmsg(sock, &s->msg, iv, il, len);
453         }
454         kfree_skb(skb);
455
456         if (len > 0) {
457                 s->stats.tx_bytes += len;
458                 s->stats.tx_packets++;
459                 return 0;
460         }
461
462         return len;
463 }
464
465 static int bnep_session(void *arg)
466 {
467         struct bnep_session *s = arg;
468         struct net_device *dev = s->dev;
469         struct sock *sk = s->sock->sk;
470         struct sk_buff *skb;
471         wait_queue_t wait;
472
473         BT_DBG("");
474
475         daemonize("kbnepd %s", dev->name);
476         set_user_nice(current, -15);
477         current->flags |= PF_NOFREEZE;
478
479         init_waitqueue_entry(&wait, current);
480         add_wait_queue(sk->sk_sleep, &wait);
481         while (!atomic_read(&s->killed)) {
482                 set_current_state(TASK_INTERRUPTIBLE);
483
484                 // RX
485                 while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
486                         skb_orphan(skb);
487                         bnep_rx_frame(s, skb);
488                 }
489
490                 if (sk->sk_state != BT_CONNECTED)
491                         break;
492
493                 // TX
494                 while ((skb = skb_dequeue(&sk->sk_write_queue)))
495                         if (bnep_tx_frame(s, skb))
496                                 break;
497                 netif_wake_queue(dev);
498
499                 schedule();
500         }
501         set_current_state(TASK_RUNNING);
502         remove_wait_queue(sk->sk_sleep, &wait);
503
504         /* Cleanup session */
505         down_write(&bnep_session_sem);
506
507         /* Delete network device */
508         unregister_netdev(dev);
509
510         /* Release the socket */
511         fput(s->sock->file);
512
513         __bnep_unlink_session(s);
514
515         up_write(&bnep_session_sem);
516         free_netdev(dev);
517         return 0;
518 }
519
520 static struct device *bnep_get_device(struct bnep_session *session)
521 {
522         bdaddr_t *src = &bt_sk(session->sock->sk)->src;
523         bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
524         struct hci_dev *hdev;
525         struct hci_conn *conn;
526
527         hdev = hci_get_route(dst, src);
528         if (!hdev)
529                 return NULL;
530
531         conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
532
533         hci_dev_put(hdev);
534
535         return conn ? &conn->dev : NULL;
536 }
537
538 int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
539 {
540         struct net_device *dev;
541         struct bnep_session *s, *ss;
542         u8 dst[ETH_ALEN], src[ETH_ALEN];
543         int err;
544
545         BT_DBG("");
546
547         baswap((void *) dst, &bt_sk(sock->sk)->dst);
548         baswap((void *) src, &bt_sk(sock->sk)->src);
549
550         /* session struct allocated as private part of net_device */
551         dev = alloc_netdev(sizeof(struct bnep_session),
552                            (*req->device) ? req->device : "bnep%d",
553                            bnep_net_setup);
554         if (!dev)
555                 return -ENOMEM;
556
557         down_write(&bnep_session_sem);
558
559         ss = __bnep_get_session(dst);
560         if (ss && ss->state == BT_CONNECTED) {
561                 err = -EEXIST;
562                 goto failed;
563         }
564
565         s = dev->priv;
566
567         /* This is rx header therefore addresses are swapped.
568          * ie eh.h_dest is our local address. */
569         memcpy(s->eh.h_dest,   &src, ETH_ALEN);
570         memcpy(s->eh.h_source, &dst, ETH_ALEN);
571         memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
572
573         s->dev   = dev;
574         s->sock  = sock;
575         s->role  = req->role;
576         s->state = BT_CONNECTED;
577
578         s->msg.msg_flags = MSG_NOSIGNAL;
579
580 #ifdef CONFIG_BT_BNEP_MC_FILTER
581         /* Set default mc filter */
582         set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
583 #endif
584
585 #ifdef CONFIG_BT_BNEP_PROTO_FILTER
586         /* Set default protocol filter */
587         bnep_set_default_proto_filter(s);
588 #endif
589
590         SET_NETDEV_DEV(dev, bnep_get_device(s));
591
592         err = register_netdev(dev);
593         if (err) {
594                 goto failed;
595         }
596
597         __bnep_link_session(s);
598
599         err = kernel_thread(bnep_session, s, CLONE_KERNEL);
600         if (err < 0) {
601                 /* Session thread start failed, gotta cleanup. */
602                 unregister_netdev(dev);
603                 __bnep_unlink_session(s);
604                 goto failed;
605         }
606
607         up_write(&bnep_session_sem);
608         strcpy(req->device, dev->name);
609         return 0;
610
611 failed:
612         up_write(&bnep_session_sem);
613         free_netdev(dev);
614         return err;
615 }
616
617 int bnep_del_connection(struct bnep_conndel_req *req)
618 {
619         struct bnep_session *s;
620         int  err = 0;
621
622         BT_DBG("");
623
624         down_read(&bnep_session_sem);
625
626         s = __bnep_get_session(req->dst);
627         if (s) {
628                 /* Wakeup user-space which is polling for socket errors.
629                  * This is temporary hack untill we have shutdown in L2CAP */
630                 s->sock->sk->sk_err = EUNATCH;
631
632                 /* Kill session thread */
633                 atomic_inc(&s->killed);
634                 wake_up_interruptible(s->sock->sk->sk_sleep);
635         } else
636                 err = -ENOENT;
637
638         up_read(&bnep_session_sem);
639         return err;
640 }
641
642 static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
643 {
644         memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
645         strcpy(ci->device, s->dev->name);
646         ci->flags = s->flags;
647         ci->state = s->state;
648         ci->role  = s->role;
649 }
650
651 int bnep_get_connlist(struct bnep_connlist_req *req)
652 {
653         struct list_head *p;
654         int err = 0, n = 0;
655
656         down_read(&bnep_session_sem);
657
658         list_for_each(p, &bnep_session_list) {
659                 struct bnep_session *s;
660                 struct bnep_conninfo ci;
661
662                 s = list_entry(p, struct bnep_session, list);
663
664                 __bnep_copy_ci(&ci, s);
665
666                 if (copy_to_user(req->ci, &ci, sizeof(ci))) {
667                         err = -EFAULT;
668                         break;
669                 }
670
671                 if (++n >= req->cnum)
672                         break;
673
674                 req->ci++;
675         }
676         req->cnum = n;
677
678         up_read(&bnep_session_sem);
679         return err;
680 }
681
682 int bnep_get_conninfo(struct bnep_conninfo *ci)
683 {
684         struct bnep_session *s;
685         int err = 0;
686
687         down_read(&bnep_session_sem);
688
689         s = __bnep_get_session(ci->dst);
690         if (s)
691                 __bnep_copy_ci(ci, s);
692         else
693                 err = -ENOENT;
694
695         up_read(&bnep_session_sem);
696         return err;
697 }
698
699 static int __init bnep_init(void)
700 {
701         char flt[50] = "";
702
703         l2cap_load();
704
705 #ifdef CONFIG_BT_BNEP_PROTO_FILTER
706         strcat(flt, "protocol ");
707 #endif
708
709 #ifdef CONFIG_BT_BNEP_MC_FILTER
710         strcat(flt, "multicast");
711 #endif
712
713         BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
714         if (flt[0])
715                 BT_INFO("BNEP filters: %s", flt);
716
717         bnep_sock_init();
718         return 0;
719 }
720
721 static void __exit bnep_exit(void)
722 {
723         bnep_sock_cleanup();
724 }
725
726 module_init(bnep_init);
727 module_exit(bnep_exit);
728
729 MODULE_AUTHOR("David Libault <david.libault@inventel.fr>, Maxim Krasnyansky <maxk@qualcomm.com>");
730 MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
731 MODULE_VERSION(VERSION);
732 MODULE_LICENSE("GPL");
733 MODULE_ALIAS("bt-proto-4");