xref: /linux/net/bluetooth/bnep/core.c (revision 79790b6818e96c58fe2bffee1b418c16e64e7b80)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds    BNEP implementation for Linux Bluetooth stack (BlueZ).
31da177e4SLinus Torvalds    Copyright (C) 2001-2002 Inventel Systemes
41da177e4SLinus Torvalds    Written 2001-2002 by
596de0e25SJan Engelhardt 	Clément Moreau <clement.moreau@inventel.fr>
61da177e4SLinus Torvalds 	David Libault  <david.libault@inventel.fr>
71da177e4SLinus Torvalds 
81da177e4SLinus Torvalds    Copyright (C) 2002 Maxim Krasnyansky <maxk@qualcomm.com>
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds    This program is free software; you can redistribute it and/or modify
111da177e4SLinus Torvalds    it under the terms of the GNU General Public License version 2 as
121da177e4SLinus Torvalds    published by the Free Software Foundation;
131da177e4SLinus Torvalds 
141da177e4SLinus Torvalds    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
151da177e4SLinus Torvalds    OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
161da177e4SLinus Torvalds    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
171da177e4SLinus Torvalds    IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
181da177e4SLinus Torvalds    CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
191da177e4SLinus Torvalds    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
201da177e4SLinus Torvalds    ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
211da177e4SLinus Torvalds    OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
221da177e4SLinus Torvalds 
231da177e4SLinus Torvalds    ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
241da177e4SLinus Torvalds    COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
251da177e4SLinus Torvalds    SOFTWARE IS DISCLAIMED.
261da177e4SLinus Torvalds */
271da177e4SLinus Torvalds 
281da177e4SLinus Torvalds #include <linux/module.h>
29f4d7cd4aSSzymon Janc #include <linux/kthread.h>
301da177e4SLinus Torvalds #include <linux/file.h>
311da177e4SLinus Torvalds #include <linux/etherdevice.h>
321da177e4SLinus Torvalds #include <asm/unaligned.h>
331da177e4SLinus Torvalds 
341da177e4SLinus Torvalds #include <net/bluetooth/bluetooth.h>
3565f53e98SMarcel Holtmann #include <net/bluetooth/l2cap.h>
360a85b964SMarcel Holtmann #include <net/bluetooth/hci_core.h>
371da177e4SLinus Torvalds 
381da177e4SLinus Torvalds #include "bnep.h"
391da177e4SLinus Torvalds 
4028111eb2SMarcel Holtmann #define VERSION "1.3"
4128111eb2SMarcel Holtmann 
42eb939922SRusty Russell static bool compress_src = true;
43eb939922SRusty Russell static bool compress_dst = true;
441da177e4SLinus Torvalds 
451da177e4SLinus Torvalds static LIST_HEAD(bnep_session_list);
461da177e4SLinus Torvalds static DECLARE_RWSEM(bnep_session_sem);
471da177e4SLinus Torvalds 
__bnep_get_session(u8 * dst)481da177e4SLinus Torvalds static struct bnep_session *__bnep_get_session(u8 *dst)
491da177e4SLinus Torvalds {
501da177e4SLinus Torvalds 	struct bnep_session *s;
511da177e4SLinus Torvalds 
521da177e4SLinus Torvalds 	BT_DBG("");
531da177e4SLinus Torvalds 
548035ded4SLuiz Augusto von Dentz 	list_for_each_entry(s, &bnep_session_list, list)
55c47fc981SJoe Perches 		if (ether_addr_equal(dst, s->eh.h_source))
561da177e4SLinus Torvalds 			return s;
578035ded4SLuiz Augusto von Dentz 
581da177e4SLinus Torvalds 	return NULL;
591da177e4SLinus Torvalds }
601da177e4SLinus Torvalds 
__bnep_link_session(struct bnep_session * s)611da177e4SLinus Torvalds static void __bnep_link_session(struct bnep_session *s)
621da177e4SLinus Torvalds {
631da177e4SLinus Torvalds 	list_add(&s->list, &bnep_session_list);
641da177e4SLinus Torvalds }
651da177e4SLinus Torvalds 
__bnep_unlink_session(struct bnep_session * s)661da177e4SLinus Torvalds static void __bnep_unlink_session(struct bnep_session *s)
671da177e4SLinus Torvalds {
681da177e4SLinus Torvalds 	list_del(&s->list);
691da177e4SLinus Torvalds }
701da177e4SLinus Torvalds 
bnep_send(struct bnep_session * s,void * data,size_t len)711da177e4SLinus Torvalds static int bnep_send(struct bnep_session *s, void *data, size_t len)
721da177e4SLinus Torvalds {
731da177e4SLinus Torvalds 	struct socket *sock = s->sock;
741da177e4SLinus Torvalds 	struct kvec iv = { data, len };
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds 	return kernel_sendmsg(sock, &s->msg, &iv, 1, len);
771da177e4SLinus Torvalds }
781da177e4SLinus Torvalds 
bnep_send_rsp(struct bnep_session * s,u8 ctrl,u16 resp)791da177e4SLinus Torvalds static int bnep_send_rsp(struct bnep_session *s, u8 ctrl, u16 resp)
801da177e4SLinus Torvalds {
811da177e4SLinus Torvalds 	struct bnep_control_rsp rsp;
821da177e4SLinus Torvalds 	rsp.type = BNEP_CONTROL;
831da177e4SLinus Torvalds 	rsp.ctrl = ctrl;
841da177e4SLinus Torvalds 	rsp.resp = htons(resp);
851da177e4SLinus Torvalds 	return bnep_send(s, &rsp, sizeof(rsp));
861da177e4SLinus Torvalds }
871da177e4SLinus Torvalds 
881da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_PROTO_FILTER
bnep_set_default_proto_filter(struct bnep_session * s)891da177e4SLinus Torvalds static inline void bnep_set_default_proto_filter(struct bnep_session *s)
901da177e4SLinus Torvalds {
911da177e4SLinus Torvalds 	/* (IPv4, ARP)  */
92e41d2169SAl Viro 	s->proto_filter[0].start = ETH_P_IP;
93e41d2169SAl Viro 	s->proto_filter[0].end   = ETH_P_ARP;
941da177e4SLinus Torvalds 	/* (RARP, AppleTalk) */
95e41d2169SAl Viro 	s->proto_filter[1].start = ETH_P_RARP;
96e41d2169SAl Viro 	s->proto_filter[1].end   = ETH_P_AARP;
971da177e4SLinus Torvalds 	/* (IPX, IPv6) */
98e41d2169SAl Viro 	s->proto_filter[2].start = ETH_P_IPX;
99e41d2169SAl Viro 	s->proto_filter[2].end   = ETH_P_IPV6;
1001da177e4SLinus Torvalds }
1011da177e4SLinus Torvalds #endif
1021da177e4SLinus Torvalds 
bnep_ctrl_set_netfilter(struct bnep_session * s,__be16 * data,int len)1031bc5d448SAl Viro static int bnep_ctrl_set_netfilter(struct bnep_session *s, __be16 *data, int len)
1041da177e4SLinus Torvalds {
1051da177e4SLinus Torvalds 	int n;
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds 	if (len < 2)
1081da177e4SLinus Torvalds 		return -EILSEQ;
1091da177e4SLinus Torvalds 
11083985319SHarvey Harrison 	n = get_unaligned_be16(data);
1113aad75a1SSzymon Janc 	data++;
1123aad75a1SSzymon Janc 	len -= 2;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	if (len < n)
1151da177e4SLinus Torvalds 		return -EILSEQ;
1161da177e4SLinus Torvalds 
1171da177e4SLinus Torvalds 	BT_DBG("filter len %d", n);
1181da177e4SLinus Torvalds 
1191da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_PROTO_FILTER
1201da177e4SLinus Torvalds 	n /= 4;
1211da177e4SLinus Torvalds 	if (n <= BNEP_MAX_PROTO_FILTERS) {
1221da177e4SLinus Torvalds 		struct bnep_proto_filter *f = s->proto_filter;
1231da177e4SLinus Torvalds 		int i;
1241da177e4SLinus Torvalds 
1251da177e4SLinus Torvalds 		for (i = 0; i < n; i++) {
12683985319SHarvey Harrison 			f[i].start = get_unaligned_be16(data++);
12783985319SHarvey Harrison 			f[i].end   = get_unaligned_be16(data++);
1281da177e4SLinus Torvalds 
1298c8ca05dSKai Ye 			BT_DBG("proto filter start %u end %u",
1301da177e4SLinus Torvalds 			       f[i].start, f[i].end);
1311da177e4SLinus Torvalds 		}
1321da177e4SLinus Torvalds 
1331da177e4SLinus Torvalds 		if (i < BNEP_MAX_PROTO_FILTERS)
1341da177e4SLinus Torvalds 			memset(f + i, 0, sizeof(*f));
1351da177e4SLinus Torvalds 
1361da177e4SLinus Torvalds 		if (n == 0)
1371da177e4SLinus Torvalds 			bnep_set_default_proto_filter(s);
1381da177e4SLinus Torvalds 
1391da177e4SLinus Torvalds 		bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_SUCCESS);
1401da177e4SLinus Torvalds 	} else {
1411da177e4SLinus Torvalds 		bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_LIMIT_REACHED);
1421da177e4SLinus Torvalds 	}
1431da177e4SLinus Torvalds #else
1441da177e4SLinus Torvalds 	bnep_send_rsp(s, BNEP_FILTER_NET_TYPE_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
1451da177e4SLinus Torvalds #endif
1461da177e4SLinus Torvalds 	return 0;
1471da177e4SLinus Torvalds }
1481da177e4SLinus Torvalds 
bnep_ctrl_set_mcfilter(struct bnep_session * s,u8 * data,int len)1491da177e4SLinus Torvalds static int bnep_ctrl_set_mcfilter(struct bnep_session *s, u8 *data, int len)
1501da177e4SLinus Torvalds {
1511da177e4SLinus Torvalds 	int n;
1521da177e4SLinus Torvalds 
1531da177e4SLinus Torvalds 	if (len < 2)
1541da177e4SLinus Torvalds 		return -EILSEQ;
1551da177e4SLinus Torvalds 
15683985319SHarvey Harrison 	n = get_unaligned_be16(data);
1573aad75a1SSzymon Janc 	data += 2;
1583aad75a1SSzymon Janc 	len -= 2;
1591da177e4SLinus Torvalds 
1601da177e4SLinus Torvalds 	if (len < n)
1611da177e4SLinus Torvalds 		return -EILSEQ;
1621da177e4SLinus Torvalds 
1631da177e4SLinus Torvalds 	BT_DBG("filter len %d", n);
1641da177e4SLinus Torvalds 
1651da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_MC_FILTER
1661da177e4SLinus Torvalds 	n /= (ETH_ALEN * 2);
1671da177e4SLinus Torvalds 
1681da177e4SLinus Torvalds 	if (n > 0) {
169a3d9bd4cSSzymon Janc 		int i;
170a3d9bd4cSSzymon Janc 
1711da177e4SLinus Torvalds 		s->mc_filter = 0;
1721da177e4SLinus Torvalds 
1731da177e4SLinus Torvalds 		/* Always send broadcast */
1741da177e4SLinus Torvalds 		set_bit(bnep_mc_hash(s->dev->broadcast), (ulong *) &s->mc_filter);
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds 		/* Add address ranges to the multicast hash */
1771da177e4SLinus Torvalds 		for (; n > 0; n--) {
1781da177e4SLinus Torvalds 			u8 a1[6], *a2;
1791da177e4SLinus Torvalds 
1803aad75a1SSzymon Janc 			memcpy(a1, data, ETH_ALEN);
1813aad75a1SSzymon Janc 			data += ETH_ALEN;
1823aad75a1SSzymon Janc 			a2 = data;
1833aad75a1SSzymon Janc 			data += ETH_ALEN;
1841da177e4SLinus Torvalds 
1856ed93dc6SAndrei Emeltchenko 			BT_DBG("mc filter %pMR -> %pMR", a1, a2);
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 			/* Iterate from a1 to a2 */
1881da177e4SLinus Torvalds 			set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
1891da177e4SLinus Torvalds 			while (memcmp(a1, a2, 6) < 0 && s->mc_filter != ~0LL) {
190a3d9bd4cSSzymon Janc 				/* Increment a1 */
191a3d9bd4cSSzymon Janc 				i = 5;
192a3d9bd4cSSzymon Janc 				while (i >= 0 && ++a1[i--] == 0)
193a3d9bd4cSSzymon Janc 					;
194a3d9bd4cSSzymon Janc 
1951da177e4SLinus Torvalds 				set_bit(bnep_mc_hash(a1), (ulong *) &s->mc_filter);
1961da177e4SLinus Torvalds 			}
1971da177e4SLinus Torvalds 		}
1981da177e4SLinus Torvalds 	}
1991da177e4SLinus Torvalds 
2001da177e4SLinus Torvalds 	BT_DBG("mc filter hash 0x%llx", s->mc_filter);
2011da177e4SLinus Torvalds 
2021da177e4SLinus Torvalds 	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_SUCCESS);
2031da177e4SLinus Torvalds #else
2041da177e4SLinus Torvalds 	bnep_send_rsp(s, BNEP_FILTER_MULTI_ADDR_RSP, BNEP_FILTER_UNSUPPORTED_REQ);
2051da177e4SLinus Torvalds #endif
2061da177e4SLinus Torvalds 	return 0;
2071da177e4SLinus Torvalds }
2081da177e4SLinus Torvalds 
bnep_rx_control(struct bnep_session * s,void * data,int len)2091da177e4SLinus Torvalds static int bnep_rx_control(struct bnep_session *s, void *data, int len)
2101da177e4SLinus Torvalds {
2111da177e4SLinus Torvalds 	u8  cmd = *(u8 *)data;
2121da177e4SLinus Torvalds 	int err = 0;
2131da177e4SLinus Torvalds 
2143aad75a1SSzymon Janc 	data++;
2153aad75a1SSzymon Janc 	len--;
2161da177e4SLinus Torvalds 
2171da177e4SLinus Torvalds 	switch (cmd) {
2181da177e4SLinus Torvalds 	case BNEP_CMD_NOT_UNDERSTOOD:
2191da177e4SLinus Torvalds 	case BNEP_SETUP_CONN_RSP:
2201da177e4SLinus Torvalds 	case BNEP_FILTER_NET_TYPE_RSP:
2211da177e4SLinus Torvalds 	case BNEP_FILTER_MULTI_ADDR_RSP:
2221da177e4SLinus Torvalds 		/* Ignore these for now */
2231da177e4SLinus Torvalds 		break;
2241da177e4SLinus Torvalds 
2251da177e4SLinus Torvalds 	case BNEP_FILTER_NET_TYPE_SET:
2261da177e4SLinus Torvalds 		err = bnep_ctrl_set_netfilter(s, data, len);
2271da177e4SLinus Torvalds 		break;
2281da177e4SLinus Torvalds 
2291da177e4SLinus Torvalds 	case BNEP_FILTER_MULTI_ADDR_SET:
2301da177e4SLinus Torvalds 		err = bnep_ctrl_set_mcfilter(s, data, len);
2311da177e4SLinus Torvalds 		break;
2321da177e4SLinus Torvalds 
233cde9f807SVikram Kandukuri 	case BNEP_SETUP_CONN_REQ:
234836a061bSGrzegorz Kolodziejczyk 		/* Successful response should be sent only once */
235836a061bSGrzegorz Kolodziejczyk 		if (test_bit(BNEP_SETUP_RESPONSE, &s->flags) &&
236836a061bSGrzegorz Kolodziejczyk 		    !test_and_set_bit(BNEP_SETUP_RSP_SENT, &s->flags))
237836a061bSGrzegorz Kolodziejczyk 			err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
238836a061bSGrzegorz Kolodziejczyk 					    BNEP_SUCCESS);
239836a061bSGrzegorz Kolodziejczyk 		else
240836a061bSGrzegorz Kolodziejczyk 			err = bnep_send_rsp(s, BNEP_SETUP_CONN_RSP,
241836a061bSGrzegorz Kolodziejczyk 					    BNEP_CONN_NOT_ALLOWED);
242cde9f807SVikram Kandukuri 		break;
243cde9f807SVikram Kandukuri 
2441da177e4SLinus Torvalds 	default: {
2451da177e4SLinus Torvalds 			u8 pkt[3];
2461da177e4SLinus Torvalds 			pkt[0] = BNEP_CONTROL;
2471da177e4SLinus Torvalds 			pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
2481da177e4SLinus Torvalds 			pkt[2] = cmd;
249e0fdbab1SGrzegorz Kolodziejczyk 			err = bnep_send(s, pkt, sizeof(pkt));
2501da177e4SLinus Torvalds 		}
2511da177e4SLinus Torvalds 		break;
2521da177e4SLinus Torvalds 	}
2531da177e4SLinus Torvalds 
2541da177e4SLinus Torvalds 	return err;
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
bnep_rx_extension(struct bnep_session * s,struct sk_buff * skb)2571da177e4SLinus Torvalds static int bnep_rx_extension(struct bnep_session *s, struct sk_buff *skb)
2581da177e4SLinus Torvalds {
2591da177e4SLinus Torvalds 	struct bnep_ext_hdr *h;
2601da177e4SLinus Torvalds 	int err = 0;
2611da177e4SLinus Torvalds 
2621da177e4SLinus Torvalds 	do {
2631da177e4SLinus Torvalds 		h = (void *) skb->data;
2641da177e4SLinus Torvalds 		if (!skb_pull(skb, sizeof(*h))) {
2651da177e4SLinus Torvalds 			err = -EILSEQ;
2661da177e4SLinus Torvalds 			break;
2671da177e4SLinus Torvalds 		}
2681da177e4SLinus Torvalds 
2698c8ca05dSKai Ye 		BT_DBG("type 0x%x len %u", h->type, h->len);
2701da177e4SLinus Torvalds 
2711da177e4SLinus Torvalds 		switch (h->type & BNEP_TYPE_MASK) {
2721da177e4SLinus Torvalds 		case BNEP_EXT_CONTROL:
2731da177e4SLinus Torvalds 			bnep_rx_control(s, skb->data, skb->len);
2741da177e4SLinus Torvalds 			break;
2751da177e4SLinus Torvalds 
2761da177e4SLinus Torvalds 		default:
2771da177e4SLinus Torvalds 			/* Unknown extension, skip it. */
2781da177e4SLinus Torvalds 			break;
2791da177e4SLinus Torvalds 		}
2801da177e4SLinus Torvalds 
2811da177e4SLinus Torvalds 		if (!skb_pull(skb, h->len)) {
2821da177e4SLinus Torvalds 			err = -EILSEQ;
2831da177e4SLinus Torvalds 			break;
2841da177e4SLinus Torvalds 		}
2851da177e4SLinus Torvalds 	} while (!err && (h->type & BNEP_EXT_HEADER));
2861da177e4SLinus Torvalds 
2871da177e4SLinus Torvalds 	return err;
2881da177e4SLinus Torvalds }
2891da177e4SLinus Torvalds 
2901da177e4SLinus Torvalds static u8 __bnep_rx_hlen[] = {
2911da177e4SLinus Torvalds 	ETH_HLEN,     /* BNEP_GENERAL */
2921da177e4SLinus Torvalds 	0,            /* BNEP_CONTROL */
2931da177e4SLinus Torvalds 	2,            /* BNEP_COMPRESSED */
2941da177e4SLinus Torvalds 	ETH_ALEN + 2, /* BNEP_COMPRESSED_SRC_ONLY */
2951da177e4SLinus Torvalds 	ETH_ALEN + 2  /* BNEP_COMPRESSED_DST_ONLY */
2961da177e4SLinus Torvalds };
2971da177e4SLinus Torvalds 
bnep_rx_frame(struct bnep_session * s,struct sk_buff * skb)2986039aa73SGustavo Padovan static int bnep_rx_frame(struct bnep_session *s, struct sk_buff *skb)
2991da177e4SLinus Torvalds {
3001da177e4SLinus Torvalds 	struct net_device *dev = s->dev;
3011da177e4SLinus Torvalds 	struct sk_buff *nskb;
302bf8b9a9cSGrzegorz Kolodziejczyk 	u8 type, ctrl_type;
3031da177e4SLinus Torvalds 
304b4d7f0a4SStephen Hemminger 	dev->stats.rx_bytes += skb->len;
3051da177e4SLinus Torvalds 
3063aad75a1SSzymon Janc 	type = *(u8 *) skb->data;
3073aad75a1SSzymon Janc 	skb_pull(skb, 1);
308bf8b9a9cSGrzegorz Kolodziejczyk 	ctrl_type = *(u8 *)skb->data;
3091da177e4SLinus Torvalds 
310a3d9bd4cSSzymon Janc 	if ((type & BNEP_TYPE_MASK) >= sizeof(__bnep_rx_hlen))
3111da177e4SLinus Torvalds 		goto badframe;
3121da177e4SLinus Torvalds 
3131da177e4SLinus Torvalds 	if ((type & BNEP_TYPE_MASK) == BNEP_CONTROL) {
314bf8b9a9cSGrzegorz Kolodziejczyk 		if (bnep_rx_control(s, skb->data, skb->len) < 0) {
315bf8b9a9cSGrzegorz Kolodziejczyk 			dev->stats.tx_errors++;
3161da177e4SLinus Torvalds 			kfree_skb(skb);
3171da177e4SLinus Torvalds 			return 0;
3181da177e4SLinus Torvalds 		}
3191da177e4SLinus Torvalds 
320bf8b9a9cSGrzegorz Kolodziejczyk 		if (!(type & BNEP_EXT_HEADER)) {
321bf8b9a9cSGrzegorz Kolodziejczyk 			kfree_skb(skb);
322bf8b9a9cSGrzegorz Kolodziejczyk 			return 0;
323bf8b9a9cSGrzegorz Kolodziejczyk 		}
324bf8b9a9cSGrzegorz Kolodziejczyk 
325bf8b9a9cSGrzegorz Kolodziejczyk 		/* Verify and pull ctrl message since it's already processed */
326bf8b9a9cSGrzegorz Kolodziejczyk 		switch (ctrl_type) {
327bf8b9a9cSGrzegorz Kolodziejczyk 		case BNEP_SETUP_CONN_REQ:
328bf8b9a9cSGrzegorz Kolodziejczyk 			/* Pull: ctrl type (1 b), len (1 b), data (len bytes) */
329bf8b9a9cSGrzegorz Kolodziejczyk 			if (!skb_pull(skb, 2 + *(u8 *)(skb->data + 1) * 2))
330bf8b9a9cSGrzegorz Kolodziejczyk 				goto badframe;
331bf8b9a9cSGrzegorz Kolodziejczyk 			break;
332bf8b9a9cSGrzegorz Kolodziejczyk 		case BNEP_FILTER_MULTI_ADDR_SET:
333bf8b9a9cSGrzegorz Kolodziejczyk 		case BNEP_FILTER_NET_TYPE_SET:
334bf8b9a9cSGrzegorz Kolodziejczyk 			/* Pull: ctrl type (1 b), len (2 b), data (len bytes) */
335bf8b9a9cSGrzegorz Kolodziejczyk 			if (!skb_pull(skb, 3 + *(u16 *)(skb->data + 1) * 2))
336bf8b9a9cSGrzegorz Kolodziejczyk 				goto badframe;
337bf8b9a9cSGrzegorz Kolodziejczyk 			break;
338bf8b9a9cSGrzegorz Kolodziejczyk 		default:
339bf8b9a9cSGrzegorz Kolodziejczyk 			kfree_skb(skb);
340bf8b9a9cSGrzegorz Kolodziejczyk 			return 0;
341bf8b9a9cSGrzegorz Kolodziejczyk 		}
342bf8b9a9cSGrzegorz Kolodziejczyk 	} else {
343459a98edSArnaldo Carvalho de Melo 		skb_reset_mac_header(skb);
3441da177e4SLinus Torvalds 
3451da177e4SLinus Torvalds 		/* Verify and pull out header */
3461da177e4SLinus Torvalds 		if (!skb_pull(skb, __bnep_rx_hlen[type & BNEP_TYPE_MASK]))
3471da177e4SLinus Torvalds 			goto badframe;
3481da177e4SLinus Torvalds 
3491bc5d448SAl Viro 		s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
350bf8b9a9cSGrzegorz Kolodziejczyk 	}
3511da177e4SLinus Torvalds 
3521da177e4SLinus Torvalds 	if (type & BNEP_EXT_HEADER) {
3531da177e4SLinus Torvalds 		if (bnep_rx_extension(s, skb) < 0)
3541da177e4SLinus Torvalds 			goto badframe;
3551da177e4SLinus Torvalds 	}
3561da177e4SLinus Torvalds 
3571da177e4SLinus Torvalds 	/* Strip 802.1p header */
358000092b0SEldad Zack 	if (ntohs(s->eh.h_proto) == ETH_P_8021Q) {
3591da177e4SLinus Torvalds 		if (!skb_pull(skb, 4))
3601da177e4SLinus Torvalds 			goto badframe;
3611bc5d448SAl Viro 		s->eh.h_proto = get_unaligned((__be16 *) (skb->data - 2));
3621da177e4SLinus Torvalds 	}
3631da177e4SLinus Torvalds 
3641da177e4SLinus Torvalds 	/* We have to alloc new skb and copy data here :(. Because original skb
3651da177e4SLinus Torvalds 	 * may not be modified and because of the alignment requirements. */
3661da177e4SLinus Torvalds 	nskb = alloc_skb(2 + ETH_HLEN + skb->len, GFP_KERNEL);
3671da177e4SLinus Torvalds 	if (!nskb) {
368b4d7f0a4SStephen Hemminger 		dev->stats.rx_dropped++;
3691da177e4SLinus Torvalds 		kfree_skb(skb);
3701da177e4SLinus Torvalds 		return -ENOMEM;
3711da177e4SLinus Torvalds 	}
3721da177e4SLinus Torvalds 	skb_reserve(nskb, 2);
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds 	/* Decompress header and construct ether frame */
3751da177e4SLinus Torvalds 	switch (type & BNEP_TYPE_MASK) {
3761da177e4SLinus Torvalds 	case BNEP_COMPRESSED:
377de77b966Syuan linyu 		__skb_put_data(nskb, &s->eh, ETH_HLEN);
3781da177e4SLinus Torvalds 		break;
3791da177e4SLinus Torvalds 
3801da177e4SLinus Torvalds 	case BNEP_COMPRESSED_SRC_ONLY:
381de77b966Syuan linyu 		__skb_put_data(nskb, s->eh.h_dest, ETH_ALEN);
382de77b966Syuan linyu 		__skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN);
3831bc5d448SAl Viro 		put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
3841da177e4SLinus Torvalds 		break;
3851da177e4SLinus Torvalds 
3861da177e4SLinus Torvalds 	case BNEP_COMPRESSED_DST_ONLY:
387de77b966Syuan linyu 		__skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN);
388*0f0639b4SLuiz Augusto von Dentz 		__skb_put_data(nskb, s->eh.h_source, ETH_ALEN);
389*0f0639b4SLuiz Augusto von Dentz 		put_unaligned(s->eh.h_proto, (__be16 *)__skb_put(nskb, 2));
3901da177e4SLinus Torvalds 		break;
3911da177e4SLinus Torvalds 
3921da177e4SLinus Torvalds 	case BNEP_GENERAL:
393de77b966Syuan linyu 		__skb_put_data(nskb, skb_mac_header(skb), ETH_ALEN * 2);
3941bc5d448SAl Viro 		put_unaligned(s->eh.h_proto, (__be16 *) __skb_put(nskb, 2));
3951da177e4SLinus Torvalds 		break;
3961da177e4SLinus Torvalds 	}
3971da177e4SLinus Torvalds 
398d626f62bSArnaldo Carvalho de Melo 	skb_copy_from_linear_data(skb, __skb_put(nskb, skb->len), skb->len);
3991da177e4SLinus Torvalds 	kfree_skb(skb);
4001da177e4SLinus Torvalds 
401b4d7f0a4SStephen Hemminger 	dev->stats.rx_packets++;
4021da177e4SLinus Torvalds 	nskb->ip_summed = CHECKSUM_NONE;
4031da177e4SLinus Torvalds 	nskb->protocol  = eth_type_trans(nskb, dev);
404d33d0dc9SSebastian Andrzej Siewior 	netif_rx(nskb);
4051da177e4SLinus Torvalds 	return 0;
4061da177e4SLinus Torvalds 
4071da177e4SLinus Torvalds badframe:
408b4d7f0a4SStephen Hemminger 	dev->stats.rx_errors++;
4091da177e4SLinus Torvalds 	kfree_skb(skb);
4101da177e4SLinus Torvalds 	return 0;
4111da177e4SLinus Torvalds }
4121da177e4SLinus Torvalds 
4131da177e4SLinus Torvalds static u8 __bnep_tx_types[] = {
4141da177e4SLinus Torvalds 	BNEP_GENERAL,
4151da177e4SLinus Torvalds 	BNEP_COMPRESSED_SRC_ONLY,
4161da177e4SLinus Torvalds 	BNEP_COMPRESSED_DST_ONLY,
4171da177e4SLinus Torvalds 	BNEP_COMPRESSED
4181da177e4SLinus Torvalds };
4191da177e4SLinus Torvalds 
bnep_tx_frame(struct bnep_session * s,struct sk_buff * skb)4206039aa73SGustavo Padovan static int bnep_tx_frame(struct bnep_session *s, struct sk_buff *skb)
4211da177e4SLinus Torvalds {
4221da177e4SLinus Torvalds 	struct ethhdr *eh = (void *) skb->data;
4231da177e4SLinus Torvalds 	struct socket *sock = s->sock;
4241da177e4SLinus Torvalds 	struct kvec iv[3];
4251da177e4SLinus Torvalds 	int len = 0, il = 0;
4261da177e4SLinus Torvalds 	u8 type = 0;
4271da177e4SLinus Torvalds 
4288c8ca05dSKai Ye 	BT_DBG("skb %p dev %p type %u", skb, skb->dev, skb->pkt_type);
4291da177e4SLinus Torvalds 
4301da177e4SLinus Torvalds 	if (!skb->dev) {
4311da177e4SLinus Torvalds 		/* Control frame sent by us */
4321da177e4SLinus Torvalds 		goto send;
4331da177e4SLinus Torvalds 	}
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	iv[il++] = (struct kvec) { &type, 1 };
4361da177e4SLinus Torvalds 	len++;
4371da177e4SLinus Torvalds 
438c47fc981SJoe Perches 	if (compress_src && ether_addr_equal(eh->h_dest, s->eh.h_source))
4391da177e4SLinus Torvalds 		type |= 0x01;
4401da177e4SLinus Torvalds 
441c47fc981SJoe Perches 	if (compress_dst && ether_addr_equal(eh->h_source, s->eh.h_dest))
4421da177e4SLinus Torvalds 		type |= 0x02;
4431da177e4SLinus Torvalds 
4441da177e4SLinus Torvalds 	if (type)
4451da177e4SLinus Torvalds 		skb_pull(skb, ETH_ALEN * 2);
4461da177e4SLinus Torvalds 
4471da177e4SLinus Torvalds 	type = __bnep_tx_types[type];
4481da177e4SLinus Torvalds 	switch (type) {
4491da177e4SLinus Torvalds 	case BNEP_COMPRESSED_SRC_ONLY:
4501da177e4SLinus Torvalds 		iv[il++] = (struct kvec) { eh->h_source, ETH_ALEN };
4511da177e4SLinus Torvalds 		len += ETH_ALEN;
4521da177e4SLinus Torvalds 		break;
4531da177e4SLinus Torvalds 
4541da177e4SLinus Torvalds 	case BNEP_COMPRESSED_DST_ONLY:
4551da177e4SLinus Torvalds 		iv[il++] = (struct kvec) { eh->h_dest, ETH_ALEN };
4561da177e4SLinus Torvalds 		len += ETH_ALEN;
4571da177e4SLinus Torvalds 		break;
4581da177e4SLinus Torvalds 	}
4591da177e4SLinus Torvalds 
4601da177e4SLinus Torvalds send:
4611da177e4SLinus Torvalds 	iv[il++] = (struct kvec) { skb->data, skb->len };
4621da177e4SLinus Torvalds 	len += skb->len;
4631da177e4SLinus Torvalds 
4641da177e4SLinus Torvalds 	/* FIXME: linearize skb */
4651da177e4SLinus Torvalds 	{
4661da177e4SLinus Torvalds 		len = kernel_sendmsg(sock, &s->msg, iv, il, len);
4671da177e4SLinus Torvalds 	}
4681da177e4SLinus Torvalds 	kfree_skb(skb);
4691da177e4SLinus Torvalds 
4701da177e4SLinus Torvalds 	if (len > 0) {
471b4d7f0a4SStephen Hemminger 		s->dev->stats.tx_bytes += len;
472b4d7f0a4SStephen Hemminger 		s->dev->stats.tx_packets++;
4731da177e4SLinus Torvalds 		return 0;
4741da177e4SLinus Torvalds 	}
4751da177e4SLinus Torvalds 
4761da177e4SLinus Torvalds 	return len;
4771da177e4SLinus Torvalds }
4781da177e4SLinus Torvalds 
bnep_session(void * arg)4791da177e4SLinus Torvalds static int bnep_session(void *arg)
4801da177e4SLinus Torvalds {
4811da177e4SLinus Torvalds 	struct bnep_session *s = arg;
4821da177e4SLinus Torvalds 	struct net_device *dev = s->dev;
4831da177e4SLinus Torvalds 	struct sock *sk = s->sock->sk;
4841da177e4SLinus Torvalds 	struct sk_buff *skb;
48525717382SJeffy Chen 	DEFINE_WAIT_FUNC(wait, woken_wake_function);
4861da177e4SLinus Torvalds 
4871da177e4SLinus Torvalds 	BT_DBG("");
4881da177e4SLinus Torvalds 
4891da177e4SLinus Torvalds 	set_user_nice(current, -15);
4901da177e4SLinus Torvalds 
491aa395145SEric Dumazet 	add_wait_queue(sk_sleep(sk), &wait);
49238d57555SPeter Hurley 	while (1) {
493751c10a5SPeter Hurley 		if (atomic_read(&s->terminate))
49438d57555SPeter Hurley 			break;
4953aad75a1SSzymon Janc 		/* RX */
4961da177e4SLinus Torvalds 		while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
4971da177e4SLinus Torvalds 			skb_orphan(skb);
49844935720SMat Martineau 			if (!skb_linearize(skb))
4991da177e4SLinus Torvalds 				bnep_rx_frame(s, skb);
50044935720SMat Martineau 			else
50144935720SMat Martineau 				kfree_skb(skb);
5021da177e4SLinus Torvalds 		}
5031da177e4SLinus Torvalds 
5041da177e4SLinus Torvalds 		if (sk->sk_state != BT_CONNECTED)
5051da177e4SLinus Torvalds 			break;
5061da177e4SLinus Torvalds 
5073aad75a1SSzymon Janc 		/* TX */
5081da177e4SLinus Torvalds 		while ((skb = skb_dequeue(&sk->sk_write_queue)))
5091da177e4SLinus Torvalds 			if (bnep_tx_frame(s, skb))
5101da177e4SLinus Torvalds 				break;
5111da177e4SLinus Torvalds 		netif_wake_queue(dev);
5121da177e4SLinus Torvalds 
5135aac4937SAndrea Parri 		/*
5145aac4937SAndrea Parri 		 * wait_woken() performs the necessary memory barriers
5155aac4937SAndrea Parri 		 * for us; see the header comment for this primitive.
5165aac4937SAndrea Parri 		 */
51725717382SJeffy Chen 		wait_woken(&wait, TASK_INTERRUPTIBLE, MAX_SCHEDULE_TIMEOUT);
5181da177e4SLinus Torvalds 	}
519aa395145SEric Dumazet 	remove_wait_queue(sk_sleep(sk), &wait);
5201da177e4SLinus Torvalds 
5211da177e4SLinus Torvalds 	/* Cleanup session */
5221da177e4SLinus Torvalds 	down_write(&bnep_session_sem);
5231da177e4SLinus Torvalds 
5241da177e4SLinus Torvalds 	/* Delete network device */
5251da177e4SLinus Torvalds 	unregister_netdev(dev);
5261da177e4SLinus Torvalds 
527ec8dab36SMarcel Holtmann 	/* Wakeup user-space polling for socket errors */
528ec8dab36SMarcel Holtmann 	s->sock->sk->sk_err = EUNATCH;
529ec8dab36SMarcel Holtmann 
530aa395145SEric Dumazet 	wake_up_interruptible(sk_sleep(s->sock->sk));
531ec8dab36SMarcel Holtmann 
5321da177e4SLinus Torvalds 	/* Release the socket */
5331da177e4SLinus Torvalds 	fput(s->sock->file);
5341da177e4SLinus Torvalds 
5351da177e4SLinus Torvalds 	__bnep_unlink_session(s);
5361da177e4SLinus Torvalds 
5371da177e4SLinus Torvalds 	up_write(&bnep_session_sem);
5381da177e4SLinus Torvalds 	free_netdev(dev);
539ca3574bdSEric W. Biederman 	module_put_and_kthread_exit(0);
5401da177e4SLinus Torvalds 	return 0;
5411da177e4SLinus Torvalds }
5421da177e4SLinus Torvalds 
bnep_get_device(struct bnep_session * session)5430a85b964SMarcel Holtmann static struct device *bnep_get_device(struct bnep_session *session)
5440a85b964SMarcel Holtmann {
54588d9077cSJohan Hedberg 	struct l2cap_conn *conn = l2cap_pi(session->sock->sk)->chan->conn;
5460a85b964SMarcel Holtmann 
54788d9077cSJohan Hedberg 	if (!conn || !conn->hcon)
5480a85b964SMarcel Holtmann 		return NULL;
5490a85b964SMarcel Holtmann 
55088d9077cSJohan Hedberg 	return &conn->hcon->dev;
5510a85b964SMarcel Holtmann }
5520a85b964SMarcel Holtmann 
553412b894aSRicardo B. Marliere static const struct device_type bnep_type = {
554384912edSMarcel Holtmann 	.name	= "bluetooth",
555384912edSMarcel Holtmann };
556384912edSMarcel Holtmann 
bnep_add_connection(struct bnep_connadd_req * req,struct socket * sock)5571da177e4SLinus Torvalds int bnep_add_connection(struct bnep_connadd_req *req, struct socket *sock)
5581da177e4SLinus Torvalds {
559836a061bSGrzegorz Kolodziejczyk 	u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
5601da177e4SLinus Torvalds 	struct net_device *dev;
5611da177e4SLinus Torvalds 	struct bnep_session *s, *ss;
5621da177e4SLinus Torvalds 	u8 dst[ETH_ALEN], src[ETH_ALEN];
5631da177e4SLinus Torvalds 	int err;
5641da177e4SLinus Torvalds 
5651da177e4SLinus Torvalds 	BT_DBG("");
5661da177e4SLinus Torvalds 
56771bb99a0SAl Viro 	if (!l2cap_is_socket(sock))
56871bb99a0SAl Viro 		return -EBADFD;
56971bb99a0SAl Viro 
5700151e426SMarcel Holtmann 	if (req->flags & ~valid_flags)
5710151e426SMarcel Holtmann 		return -EINVAL;
5720151e426SMarcel Holtmann 
57365f53e98SMarcel Holtmann 	baswap((void *) dst, &l2cap_pi(sock->sk)->chan->dst);
57465f53e98SMarcel Holtmann 	baswap((void *) src, &l2cap_pi(sock->sk)->chan->src);
5751da177e4SLinus Torvalds 
5761da177e4SLinus Torvalds 	/* session struct allocated as private part of net_device */
5771da177e4SLinus Torvalds 	dev = alloc_netdev(sizeof(struct bnep_session),
5781da177e4SLinus Torvalds 			   (*req->device) ? req->device : "bnep%d",
579c835a677STom Gundersen 			   NET_NAME_UNKNOWN,
5801da177e4SLinus Torvalds 			   bnep_net_setup);
5811da177e4SLinus Torvalds 	if (!dev)
58267b52e55STobias Klauser 		return -ENOMEM;
5831da177e4SLinus Torvalds 
5841da177e4SLinus Torvalds 	down_write(&bnep_session_sem);
5851da177e4SLinus Torvalds 
5861da177e4SLinus Torvalds 	ss = __bnep_get_session(dst);
5871da177e4SLinus Torvalds 	if (ss && ss->state == BT_CONNECTED) {
5881da177e4SLinus Torvalds 		err = -EEXIST;
5891da177e4SLinus Torvalds 		goto failed;
5901da177e4SLinus Torvalds 	}
5911da177e4SLinus Torvalds 
592524ad0a7SWang Chen 	s = netdev_priv(dev);
5931da177e4SLinus Torvalds 
5941da177e4SLinus Torvalds 	/* This is rx header therefore addresses are swapped.
5953aad75a1SSzymon Janc 	 * ie. eh.h_dest is our local address. */
5961da177e4SLinus Torvalds 	memcpy(s->eh.h_dest,   &src, ETH_ALEN);
5971da177e4SLinus Torvalds 	memcpy(s->eh.h_source, &dst, ETH_ALEN);
59808c181f0SJakub Kicinski 	eth_hw_addr_set(dev, s->eh.h_dest);
5991da177e4SLinus Torvalds 
6001da177e4SLinus Torvalds 	s->dev   = dev;
6011da177e4SLinus Torvalds 	s->sock  = sock;
6021da177e4SLinus Torvalds 	s->role  = req->role;
6031da177e4SLinus Torvalds 	s->state = BT_CONNECTED;
604836a061bSGrzegorz Kolodziejczyk 	s->flags = req->flags;
6051da177e4SLinus Torvalds 
6061da177e4SLinus Torvalds 	s->msg.msg_flags = MSG_NOSIGNAL;
6071da177e4SLinus Torvalds 
6081da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_MC_FILTER
6094ada1282SDanny Schweizer 	/* Set default mc filter to not filter out any mc addresses
6104ada1282SDanny Schweizer 	 * as defined in the BNEP specification (revision 0.95a)
6114ada1282SDanny Schweizer 	 * http://grouper.ieee.org/groups/802/15/Bluetooth/BNEP.pdf
6124ada1282SDanny Schweizer 	 */
6134ada1282SDanny Schweizer 	s->mc_filter = ~0LL;
6141da177e4SLinus Torvalds #endif
6151da177e4SLinus Torvalds 
6161da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_PROTO_FILTER
6171da177e4SLinus Torvalds 	/* Set default protocol filter */
6181da177e4SLinus Torvalds 	bnep_set_default_proto_filter(s);
6191da177e4SLinus Torvalds #endif
6201da177e4SLinus Torvalds 
6210a85b964SMarcel Holtmann 	SET_NETDEV_DEV(dev, bnep_get_device(s));
622384912edSMarcel Holtmann 	SET_NETDEV_DEVTYPE(dev, &bnep_type);
6230a85b964SMarcel Holtmann 
6241da177e4SLinus Torvalds 	err = register_netdev(dev);
6253aad75a1SSzymon Janc 	if (err)
6261da177e4SLinus Torvalds 		goto failed;
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds 	__bnep_link_session(s);
6291da177e4SLinus Torvalds 
6309b338c3dSDavid Herrmann 	__module_get(THIS_MODULE);
631f4d7cd4aSSzymon Janc 	s->task = kthread_run(bnep_session, s, "kbnepd %s", dev->name);
632f4d7cd4aSSzymon Janc 	if (IS_ERR(s->task)) {
6331da177e4SLinus Torvalds 		/* Session thread start failed, gotta cleanup. */
6349b338c3dSDavid Herrmann 		module_put(THIS_MODULE);
6351da177e4SLinus Torvalds 		unregister_netdev(dev);
6361da177e4SLinus Torvalds 		__bnep_unlink_session(s);
637f4d7cd4aSSzymon Janc 		err = PTR_ERR(s->task);
6381da177e4SLinus Torvalds 		goto failed;
6391da177e4SLinus Torvalds 	}
6401da177e4SLinus Torvalds 
6411da177e4SLinus Torvalds 	up_write(&bnep_session_sem);
6421da177e4SLinus Torvalds 	strcpy(req->device, dev->name);
6431da177e4SLinus Torvalds 	return 0;
6441da177e4SLinus Torvalds 
6451da177e4SLinus Torvalds failed:
6461da177e4SLinus Torvalds 	up_write(&bnep_session_sem);
6471da177e4SLinus Torvalds 	free_netdev(dev);
6481da177e4SLinus Torvalds 	return err;
6491da177e4SLinus Torvalds }
6501da177e4SLinus Torvalds 
bnep_del_connection(struct bnep_conndel_req * req)6511da177e4SLinus Torvalds int bnep_del_connection(struct bnep_conndel_req *req)
6521da177e4SLinus Torvalds {
6530151e426SMarcel Holtmann 	u32 valid_flags = 0;
6541da177e4SLinus Torvalds 	struct bnep_session *s;
6551da177e4SLinus Torvalds 	int  err = 0;
6561da177e4SLinus Torvalds 
6571da177e4SLinus Torvalds 	BT_DBG("");
6581da177e4SLinus Torvalds 
6590151e426SMarcel Holtmann 	if (req->flags & ~valid_flags)
6600151e426SMarcel Holtmann 		return -EINVAL;
6610151e426SMarcel Holtmann 
6621da177e4SLinus Torvalds 	down_read(&bnep_session_sem);
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds 	s = __bnep_get_session(req->dst);
665751c10a5SPeter Hurley 	if (s) {
666751c10a5SPeter Hurley 		atomic_inc(&s->terminate);
66725717382SJeffy Chen 		wake_up_interruptible(sk_sleep(s->sock->sk));
668751c10a5SPeter Hurley 	} else
6691da177e4SLinus Torvalds 		err = -ENOENT;
6701da177e4SLinus Torvalds 
6711da177e4SLinus Torvalds 	up_read(&bnep_session_sem);
6721da177e4SLinus Torvalds 	return err;
6731da177e4SLinus Torvalds }
6741da177e4SLinus Torvalds 
__bnep_copy_ci(struct bnep_conninfo * ci,struct bnep_session * s)6751da177e4SLinus Torvalds static void __bnep_copy_ci(struct bnep_conninfo *ci, struct bnep_session *s)
6761da177e4SLinus Torvalds {
677836a061bSGrzegorz Kolodziejczyk 	u32 valid_flags = BIT(BNEP_SETUP_RESPONSE);
6780151e426SMarcel Holtmann 
6795520d20fSVasiliy Kulikov 	memset(ci, 0, sizeof(*ci));
6801da177e4SLinus Torvalds 	memcpy(ci->dst, s->eh.h_source, ETH_ALEN);
6811da177e4SLinus Torvalds 	strcpy(ci->device, s->dev->name);
6820151e426SMarcel Holtmann 	ci->flags = s->flags & valid_flags;
6831da177e4SLinus Torvalds 	ci->state = s->state;
6841da177e4SLinus Torvalds 	ci->role  = s->role;
6851da177e4SLinus Torvalds }
6861da177e4SLinus Torvalds 
bnep_get_connlist(struct bnep_connlist_req * req)6871da177e4SLinus Torvalds int bnep_get_connlist(struct bnep_connlist_req *req)
6881da177e4SLinus Torvalds {
6898035ded4SLuiz Augusto von Dentz 	struct bnep_session *s;
6901da177e4SLinus Torvalds 	int err = 0, n = 0;
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds 	down_read(&bnep_session_sem);
6931da177e4SLinus Torvalds 
6948035ded4SLuiz Augusto von Dentz 	list_for_each_entry(s, &bnep_session_list, list) {
6951da177e4SLinus Torvalds 		struct bnep_conninfo ci;
6961da177e4SLinus Torvalds 
6971da177e4SLinus Torvalds 		__bnep_copy_ci(&ci, s);
6981da177e4SLinus Torvalds 
6991da177e4SLinus Torvalds 		if (copy_to_user(req->ci, &ci, sizeof(ci))) {
7001da177e4SLinus Torvalds 			err = -EFAULT;
7011da177e4SLinus Torvalds 			break;
7021da177e4SLinus Torvalds 		}
7031da177e4SLinus Torvalds 
7041da177e4SLinus Torvalds 		if (++n >= req->cnum)
7051da177e4SLinus Torvalds 			break;
7061da177e4SLinus Torvalds 
7071da177e4SLinus Torvalds 		req->ci++;
7081da177e4SLinus Torvalds 	}
7091da177e4SLinus Torvalds 	req->cnum = n;
7101da177e4SLinus Torvalds 
7111da177e4SLinus Torvalds 	up_read(&bnep_session_sem);
7121da177e4SLinus Torvalds 	return err;
7131da177e4SLinus Torvalds }
7141da177e4SLinus Torvalds 
bnep_get_conninfo(struct bnep_conninfo * ci)7151da177e4SLinus Torvalds int bnep_get_conninfo(struct bnep_conninfo *ci)
7161da177e4SLinus Torvalds {
7171da177e4SLinus Torvalds 	struct bnep_session *s;
7181da177e4SLinus Torvalds 	int err = 0;
7191da177e4SLinus Torvalds 
7201da177e4SLinus Torvalds 	down_read(&bnep_session_sem);
7211da177e4SLinus Torvalds 
7221da177e4SLinus Torvalds 	s = __bnep_get_session(ci->dst);
7231da177e4SLinus Torvalds 	if (s)
7241da177e4SLinus Torvalds 		__bnep_copy_ci(ci, s);
7251da177e4SLinus Torvalds 	else
7261da177e4SLinus Torvalds 		err = -ENOENT;
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds 	up_read(&bnep_session_sem);
7291da177e4SLinus Torvalds 	return err;
7301da177e4SLinus Torvalds }
7311da177e4SLinus Torvalds 
bnep_init(void)7321da177e4SLinus Torvalds static int __init bnep_init(void)
7331da177e4SLinus Torvalds {
7341da177e4SLinus Torvalds 	char flt[50] = "";
7351da177e4SLinus Torvalds 
7361da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_PROTO_FILTER
7371da177e4SLinus Torvalds 	strcat(flt, "protocol ");
7381da177e4SLinus Torvalds #endif
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds #ifdef CONFIG_BT_BNEP_MC_FILTER
7411da177e4SLinus Torvalds 	strcat(flt, "multicast");
7421da177e4SLinus Torvalds #endif
7431da177e4SLinus Torvalds 
7441da177e4SLinus Torvalds 	BT_INFO("BNEP (Ethernet Emulation) ver %s", VERSION);
7451da177e4SLinus Torvalds 	if (flt[0])
7461da177e4SLinus Torvalds 		BT_INFO("BNEP filters: %s", flt);
7471da177e4SLinus Torvalds 
7481da177e4SLinus Torvalds 	bnep_sock_init();
7491da177e4SLinus Torvalds 	return 0;
7501da177e4SLinus Torvalds }
7511da177e4SLinus Torvalds 
bnep_exit(void)7521da177e4SLinus Torvalds static void __exit bnep_exit(void)
7531da177e4SLinus Torvalds {
7541da177e4SLinus Torvalds 	bnep_sock_cleanup();
7551da177e4SLinus Torvalds }
7561da177e4SLinus Torvalds 
7571da177e4SLinus Torvalds module_init(bnep_init);
7581da177e4SLinus Torvalds module_exit(bnep_exit);
7591da177e4SLinus Torvalds 
76028111eb2SMarcel Holtmann module_param(compress_src, bool, 0644);
76128111eb2SMarcel Holtmann MODULE_PARM_DESC(compress_src, "Compress sources headers");
76228111eb2SMarcel Holtmann 
76328111eb2SMarcel Holtmann module_param(compress_dst, bool, 0644);
76428111eb2SMarcel Holtmann MODULE_PARM_DESC(compress_dst, "Compress destination headers");
76528111eb2SMarcel Holtmann 
76663fbd24eSMarcel Holtmann MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
7671da177e4SLinus Torvalds MODULE_DESCRIPTION("Bluetooth BNEP ver " VERSION);
7681da177e4SLinus Torvalds MODULE_VERSION(VERSION);
7691da177e4SLinus Torvalds MODULE_LICENSE("GPL");
7701da177e4SLinus Torvalds MODULE_ALIAS("bt-proto-4");
771