• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /asuswrt-rt-n18u-9.0.0.4.380.2695/release/src-rt-6.x.4708/linux/linux-2.6.36/net/bluetooth/bnep/
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#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>
35#include <linux/freezer.h>
36#include <linux/errno.h>
37#include <linux/net.h>
38#include <linux/slab.h>
39#include <net/sock.h>
40
41#include <linux/socket.h>
42#include <linux/file.h>
43
44#include <linux/netdevice.h>
45#include <linux/etherdevice.h>
46#include <linux/skbuff.h>
47
48#include <asm/unaligned.h>
49
50#include <net/bluetooth/bluetooth.h>
51#include <net/bluetooth/hci_core.h>
52#include <net/bluetooth/l2cap.h>
53
54#include "bnep.h"
55
56#define VERSION "1.3"
57
58static int compress_src = 1;
59static int compress_dst = 1;
60
61static LIST_HEAD(bnep_session_list);
62static DECLARE_RWSEM(bnep_session_sem);
63
64static struct bnep_session *__bnep_get_session(u8 *dst)
65{
66	struct bnep_session *s;
67	struct list_head *p;
68
69	BT_DBG("");
70
71	list_for_each(p, &bnep_session_list) {
72		s = list_entry(p, struct bnep_session, list);
73		if (!compare_ether_addr(dst, s->eh.h_source))
74			return s;
75	}
76	return NULL;
77}
78
79static void __bnep_link_session(struct bnep_session *s)
80{
81	/* It's safe to call __module_get() here because sessions are added
82	   by the socket layer which has to hold the reference to this module.
83	 */
84	__module_get(THIS_MODULE);
85	list_add(&s->list, &bnep_session_list);
86}
87
88static void __bnep_unlink_session(struct bnep_session *s)
89{
90	list_del(&s->list);
91	module_put(THIS_MODULE);
92}
93
94static int bnep_send(struct bnep_session *s, void *data, size_t len)
95{
96	struct socket *sock = s->sock;
97	struct kvec iv = { data, len };
98
99	return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
100}
101
102static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
103{
104	struct bnep_control_rsp rsp;
105	rsp.type = BNEP_CONTROL;
106	rsp.ctrl = ctrl;
107	rsp.resp = htons(resp);
108	return bnep_send(s, &rsp, sizeof(rsp));
109}
110
111#ifdef CONFIG_BT_BNEP_PROTO_FILTER
112static inline void bnep_set_default_proto_filter(struct bnep_session *s)
113{
114	/* (IPv4, ARP)  */
115	s->proto_filter[0].start = ETH_P_IP;
116	s->proto_filter[0].end   = ETH_P_ARP;
117	/* (RARP, AppleTalk) */
118	s->proto_filter[1].start = ETH_P_RARP;
119	s->proto_filter[1].end   = ETH_P_AARP;
120	/* (IPX, IPv6) */
121	s->proto_filter[2].start = ETH_P_IPX;
122	s->proto_filter[2].end   = ETH_P_IPV6;
123}
124#endif
125
126static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
127{
128	int n;
129
130	if (len < 2)
131		return -EILSEQ;
132
133	n = get_unaligned_be16(data);
134	data++; len -= 2;
135
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++) {
148			f[i].start = get_unaligned_be16(data++);
149			f[i].end   = get_unaligned_be16(data++);
150
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
178	n = get_unaligned_be16(data);
179	data += 2; len -= 2;
180
181	if (len < n)
182		return -EILSEQ;
183
184	BT_DBG("filter len %d", n);
185
186#ifdef CONFIG_BT_BNEP_MC_FILTER
187	n /= (ETH_ALEN * 2);
188
189	if (n > 0) {
190		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
199			memcpy(a1, data, ETH_ALEN); data += ETH_ALEN;
200			a2 = data; data += ETH_ALEN;
201
202			BT_DBG("mc filter %s -> %s",
203				batostr((void *) a1), batostr((void *) a2));
204
205			#define INCA(a) { int i = 5; while (i >=0 && ++a[i--] == 0); }
206
207			/* 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) {
210				INCA(a1);
211				set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
212			}
213		}
214	}
215
216	BT_DBG("mc filter hash 0x%llx", s->mc_filter);
217
218	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
219#else
220	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
221#endif
222	return 0;
223}
224
225static int bnep_rx_control(struct bnep_session *s, void *data, int len)
226{
227	u8  cmd = *(u8 *)data;
228	int err = 0;
229
230	data++; len--;
231
232	switch (cmd) {
233	case BNEP_CMD_NOT_UNDERSTOOD:
234	case BNEP_SETUP_CONN_RSP:
235	case BNEP_FILTER_NET_TYPE_RSP:
236	case BNEP_FILTER_MULTI_ADDR_RSP:
237		/* Ignore these for now */
238		break;
239
240	case BNEP_FILTER_NET_TYPE_SET:
241		err = bnep_ctrl_set_netfilter(s, data, len);
242		break;
243
244	case BNEP_FILTER_MULTI_ADDR_SET:
245		err = bnep_ctrl_set_mcfilter(s, data, len);
246		break;
247
248	case BNEP_SETUP_CONN_REQ:
249		err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP, BNEP_CONN_NOT_ALLOWED);
250		break;
251
252	default: {
253			u8 pkt[3];
254			pkt[0] = BNEP_CONTROL;
255			pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
256			pkt[2] = cmd;
257			bnep_send(s, pkt, sizeof(pkt));
258		}
259		break;
260	}
261
262	return err;
263}
264
265static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
266{
267	struct bnep_ext_hdr *h;
268	int err = 0;
269
270	do {
271		h = (void *) skb->data;
272		if (!skb_pull(skb, sizeof(*h))) {
273			err = -EILSEQ;
274			break;
275		}
276
277		BT_DBG("type 0x%x len %d", h->type, h->len);
278
279		switch (h->type & BNEP_TYPE_MASK) {
280		case BNEP_EXT_CONTROL:
281			bnep_rx_control(s, skb->data, skb->len);
282			break;
283
284		default:
285			/* Unknown extension, skip it. */
286			break;
287		}
288
289		if (!skb_pull(skb, h->len)) {
290			err = -EILSEQ;
291			break;
292		}
293	} while (!err && (h->type & BNEP_EXT_HEADER));
294
295	return err;
296}
297
298static u8 __bnep_rx_hlen[] = {
299	ETH_HLEN,     /* BNEP_GENERAL */
300	0,            /* BNEP_CONTROL */
301	2,            /* BNEP_COMPRESSED */
302	ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
303	ETH_ALEN + 2  /* BNEP_COMPRESSED_DST_ONLY */
304};
305#define BNEP_RX_TYPES	(sizeof(__bnep_rx_hlen) - 1)
306
307static inline int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
308{
309	struct net_device *dev = s->dev;
310	struct sk_buff *nskb;
311	u8 type;
312
313	dev->stats.rx_bytes += skb->len;
314
315	type = *(u8 *) skb->data; skb_pull(skb, 1);
316
317	if ((type & BNEP_TYPE_MASK) > BNEP_RX_TYPES)
318		goto badframe;
319
320	if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
321		bnep_rx_control(s, skb->data, skb->len);
322		kfree_skb(skb);
323		return 0;
324	}
325
326	skb_reset_mac_header(skb);
327
328	/* Verify and pull out header */
329	if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
330		goto badframe;
331
332	s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
333
334	if (type & BNEP_EXT_HEADER) {
335		if (bnep_rx_extension(s, skb) < 0)
336			goto badframe;
337	}
338
339	/* Strip 802.1p header */
340	if (ntohs(s->eh.h_proto) == 0x8100) {
341		if (!skb_pull(skb, 4))
342			goto badframe;
343		s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
344	}
345
346	/* We have to alloc new skb and copy data here :(. Because original skb
347	 * may not be modified and because of the alignment requirements. */
348	nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
349	if (!nskb) {
350		dev->stats.rx_dropped++;
351		kfree_skb(skb);
352		return -ENOMEM;
353	}
354	skb_reserve(nskb, 2);
355
356	/* Decompress header and construct ether frame */
357	switch (type & BNEP_TYPE_MASK) {
358	case BNEP_COMPRESSED:
359		memcpy(__skb_put(nskb, ETH_HLEN), &s->eh, ETH_HLEN);
360		break;
361
362	case BNEP_COMPRESSED_SRC_ONLY:
363		memcpy(__skb_put(nskb, ETH_ALEN), s->eh.h_dest, ETH_ALEN);
364		memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb), ETH_ALEN);
365		put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
366		break;
367
368	case BNEP_COMPRESSED_DST_ONLY:
369		memcpy(__skb_put(nskb, ETH_ALEN), skb_mac_header(skb),
370		       ETH_ALEN);
371		memcpy(__skb_put(nskb, ETH_ALEN + 2), s->eh.h_source,
372		       ETH_ALEN + 2);
373		break;
374
375	case BNEP_GENERAL:
376		memcpy(__skb_put(nskb, ETH_ALEN * 2), skb_mac_header(skb),
377		       ETH_ALEN * 2);
378		put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
379		break;
380	}
381
382	skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
383	kfree_skb(skb);
384
385	dev->stats.rx_packets++;
386	nskb->ip_summed = CHECKSUM_NONE;
387	nskb->protocol  = eth_type_trans(nskb, dev);
388	netif_rx_ni(nskb);
389	return 0;
390
391badframe:
392	dev->stats.rx_errors++;
393	kfree_skb(skb);
394	return 0;
395}
396
397static u8 __bnep_tx_types[] = {
398	BNEP_GENERAL,
399	BNEP_COMPRESSED_SRC_ONLY,
400	BNEP_COMPRESSED_DST_ONLY,
401	BNEP_COMPRESSED
402};
403
404static inline int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
405{
406	struct ethhdr *eh = (void *) skb->data;
407	struct socket *sock = s->sock;
408	struct kvec iv[3];
409	int len = 0, il = 0;
410	u8 type = 0;
411
412	BT_DBG("skb %p dev %p type %d", skb, skb->dev, skb->pkt_type);
413
414	if (!skb->dev) {
415		/* Control frame sent by us */
416		goto send;
417	}
418
419	iv[il++] = (struct kvec) { &type, 1 };
420	len++;
421
422	if (compress_src && !compare_ether_addr(eh->h_dest, s->eh.h_source))
423		type |= 0x01;
424
425	if (compress_dst && !compare_ether_addr(eh->h_source, s->eh.h_dest))
426		type |= 0x02;
427
428	if (type)
429		skb_pull(skb, ETH_ALEN * 2);
430
431	type = __bnep_tx_types[type];
432	switch (type) {
433	case BNEP_COMPRESSED_SRC_ONLY:
434		iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
435		len += ETH_ALEN;
436		break;
437
438	case BNEP_COMPRESSED_DST_ONLY:
439		iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
440		len += ETH_ALEN;
441		break;
442	}
443
444send:
445	iv[il++] = (struct kvec) { skb->data, skb->len };
446	len += skb->len;
447
448	{
449		len = kernel_sendmsg(sock, &s->msg, iv, il, len);
450	}
451	kfree_skb(skb);
452
453	if (len > 0) {
454		s->dev->stats.tx_bytes += len;
455		s->dev->stats.tx_packets++;
456		return 0;
457	}
458
459	return len;
460}
461
462static int bnep_session(void *arg)
463{
464	struct bnep_session *s = arg;
465	struct net_device *dev = s->dev;
466	struct sock *sk = s->sock->sk;
467	struct sk_buff *skb;
468	wait_queue_t wait;
469
470	BT_DBG("");
471
472	daemonize("kbnepd %s", dev->name);
473	set_user_nice(current, -15);
474
475	init_waitqueue_entry(&wait, current);
476	add_wait_queue(sk_sleep(sk), &wait);
477	while (!atomic_read(&s->killed)) {
478		set_current_state(TASK_INTERRUPTIBLE);
479
480		// RX
481		while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
482			skb_orphan(skb);
483			bnep_rx_frame(s, skb);
484		}
485
486		if (sk->sk_state != BT_CONNECTED)
487			break;
488
489		// TX
490		while ((skb = skb_dequeue(&sk->sk_write_queue)))
491			if (bnep_tx_frame(s, skb))
492				break;
493		netif_wake_queue(dev);
494
495		schedule();
496	}
497	set_current_state(TASK_RUNNING);
498	remove_wait_queue(sk_sleep(sk), &wait);
499
500	/* Cleanup session */
501	down_write(&bnep_session_sem);
502
503	/* Delete network device */
504	unregister_netdev(dev);
505
506	/* Wakeup user-space polling for socket errors */
507	s->sock->sk->sk_err = EUNATCH;
508
509	wake_up_interruptible(sk_sleep(s->sock->sk));
510
511	/* Release the socket */
512	fput(s->sock->file);
513
514	__bnep_unlink_session(s);
515
516	up_write(&bnep_session_sem);
517	free_netdev(dev);
518	return 0;
519}
520
521static struct device *bnep_get_device(struct bnep_session *session)
522{
523	bdaddr_t *src = &bt_sk(session->sock->sk)->src;
524	bdaddr_t *dst = &bt_sk(session->sock->sk)->dst;
525	struct hci_dev *hdev;
526	struct hci_conn *conn;
527
528	hdev = hci_get_route(dst, src);
529	if (!hdev)
530		return NULL;
531
532	conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, dst);
533
534	hci_dev_put(hdev);
535
536	return conn ? &conn->dev : NULL;
537}
538
539static struct device_type bnep_type = {
540	.name	= "bluetooth",
541};
542
543int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
544{
545	struct net_device *dev;
546	struct bnep_session *s, *ss;
547	u8 dst[ETH_ALEN], src[ETH_ALEN];
548	int err;
549
550	BT_DBG("");
551
552	baswap((void *) dst, &bt_sk(sock->sk)->dst);
553	baswap((void *) src, &bt_sk(sock->sk)->src);
554
555	/* session struct allocated as private part of net_device */
556	dev = alloc_netdev(sizeof(struct bnep_session),
557			   (*req->device) ? req->device : "bnep%d",
558			   bnep_net_setup);
559	if (!dev)
560		return -ENOMEM;
561
562	down_write(&bnep_session_sem);
563
564	ss = __bnep_get_session(dst);
565	if (ss && ss->state == BT_CONNECTED) {
566		err = -EEXIST;
567		goto failed;
568	}
569
570	s = netdev_priv(dev);
571
572	/* This is rx header therefore addresses are swapped.
573	 * ie eh.h_dest is our local address. */
574	memcpy(s->eh.h_dest,   &src, ETH_ALEN);
575	memcpy(s->eh.h_source, &dst, ETH_ALEN);
576	memcpy(dev->dev_addr, s->eh.h_dest, ETH_ALEN);
577
578	s->dev   = dev;
579	s->sock  = sock;
580	s->role  = req->role;
581	s->state = BT_CONNECTED;
582
583	s->msg.msg_flags = MSG_NOSIGNAL;
584
585#ifdef CONFIG_BT_BNEP_MC_FILTER
586	/* Set default mc filter */
587	set_bit(bnep_mc_hash(dev->broadcast), (ulong *) &s->mc_filter);
588#endif
589
590#ifdef CONFIG_BT_BNEP_PROTO_FILTER
591	/* Set default protocol filter */
592	bnep_set_default_proto_filter(s);
593#endif
594
595	SET_NETDEV_DEV(dev, bnep_get_device(s));
596	SET_NETDEV_DEVTYPE(dev, &bnep_type);
597
598	err = register_netdev(dev);
599	if (err) {
600		goto failed;
601	}
602
603	__bnep_link_session(s);
604
605	err = kernel_thread(bnep_session, s, CLONE_KERNEL);
606	if (err < 0) {
607		/* Session thread start failed, gotta cleanup. */
608		unregister_netdev(dev);
609		__bnep_unlink_session(s);
610		goto failed;
611	}
612
613	up_write(&bnep_session_sem);
614	strcpy(req->device, dev->name);
615	return 0;
616
617failed:
618	up_write(&bnep_session_sem);
619	free_netdev(dev);
620	return err;
621}
622
623int bnep_del_connection(struct bnep_conndel_req *req)
624{
625	struct bnep_session *s;
626	int  err = 0;
627
628	BT_DBG("");
629
630	down_read(&bnep_session_sem);
631
632	s = __bnep_get_session(req->dst);
633	if (s) {
634		/* Wakeup user-space which is polling for socket errors.
635		 * This is temporary hack until we have shutdown in L2CAP */
636		s->sock->sk->sk_err = EUNATCH;
637
638		/* Kill session thread */
639		atomic_inc(&s->killed);
640		wake_up_interruptible(sk_sleep(s->sock->sk));
641	} else
642		err = -ENOENT;
643
644	up_read(&bnep_session_sem);
645	return err;
646}
647
648static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
649{
650	memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
651	strcpy(ci->device, s->dev->name);
652	ci->flags = s->flags;
653	ci->state = s->state;
654	ci->role  = s->role;
655}
656
657int bnep_get_connlist(struct bnep_connlist_req *req)
658{
659	struct list_head *p;
660	int err = 0, n = 0;
661
662	down_read(&bnep_session_sem);
663
664	list_for_each(p, &bnep_session_list) {
665		struct bnep_session *s;
666		struct bnep_conninfo ci;
667
668		s = list_entry(p, struct bnep_session, list);
669
670		__bnep_copy_ci(&ci, s);
671
672		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
673			err = -EFAULT;
674			break;
675		}
676
677		if (++n >= req->cnum)
678			break;
679
680		req->ci++;
681	}
682	req->cnum = n;
683
684	up_read(&bnep_session_sem);
685	return err;
686}
687
688int bnep_get_conninfo(struct bnep_conninfo *ci)
689{
690	struct bnep_session *s;
691	int err = 0;
692
693	down_read(&bnep_session_sem);
694
695	s = __bnep_get_session(ci->dst);
696	if (s)
697		__bnep_copy_ci(ci, s);
698	else
699		err = -ENOENT;
700
701	up_read(&bnep_session_sem);
702	return err;
703}
704
705static int __init bnep_init(void)
706{
707	char flt[50] = "";
708
709	l2cap_load();
710
711#ifdef CONFIG_BT_BNEP_PROTO_FILTER
712	strcat(flt, "protocol ");
713#endif
714
715#ifdef CONFIG_BT_BNEP_MC_FILTER
716	strcat(flt, "multicast");
717#endif
718
719	BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
720	if (flt[0])
721		BT_INFO("BNEP filters: %s", flt);
722
723	bnep_sock_init();
724	return 0;
725}
726
727static void __exit bnep_exit(void)
728{
729	bnep_sock_cleanup();
730}
731
732module_init(bnep_init);
733module_exit(bnep_exit);
734
735module_param(compress_src, bool, 0644);
736MODULE_PARM_DESC(compress_src, "Compress sources headers");
737
738module_param(compress_dst, bool, 0644);
739MODULE_PARM_DESC(compress_dst, "Compress destination headers");
740
741MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
742MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
743MODULE_VERSION(VERSION);
744MODULE_LICENSE("GPL");
745MODULE_ALIAS("bt-proto-4");
746