Linux bluetooth development
 help / color / mirror / Atom feed
* [RFC v2 6/7] Bluetooth: Set route to peer for 6LoWPAN
From: Jukka Rissanen @ 2013-10-30  9:01 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1383123661-15087-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 net/bluetooth/6lowpan.c | 37 +++++++++++++++++++++++++++++++++++++
 1 file changed, 37 insertions(+)

diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 1cba7fd..4ec3813 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -1372,6 +1372,39 @@ static void set_dev_addr(struct net_device *net, bdaddr_t *addr,
 	net->dev_addr[0] ^= 2;
 }
 
+static int add_peer_route(struct lowpan_info *dev, struct net_device *net,
+			bdaddr_t *addr, u8 addr_type)
+{
+	struct in6_addr peer;
+	struct fib6_config cfg = {
+		.fc_table	= RT6_TABLE_MAIN,
+		.fc_metric	= IP6_RT_PRIO_ADDRCONF,
+		.fc_ifindex	= net->ifindex,
+		.fc_dst_len	= 128,
+		.fc_flags	= RTF_ADDRCONF | RTF_UP |
+					RTF_PREF(ICMPV6_ROUTER_PREF_MEDIUM),
+		.fc_nlinfo.portid = 0,
+		.fc_nlinfo.nlh = NULL,
+		.fc_nlinfo.nl_net = dev_net(net),
+	};
+
+	memset(&peer, 0, sizeof(struct in6_addr));
+
+	/* RFC 2464 ch. 5 */
+	peer.s6_addr[0] = 0xFE;
+	peer.s6_addr[1] = 0x80;
+	set_addr((u8 *)&peer.s6_addr + 8, addr->b, addr_type);
+
+	memcpy(&dev->ieee802154_addr, (u8 *)&peer.s6_addr + 8,
+						IEEE802154_ADDR_LEN);
+
+	BT_DBG("peer address %pI6c", (u8 *)&peer.s6_addr);
+
+	cfg.fc_dst = dev->peer = peer;
+
+	return ip6_route_add(&cfg);
+}
+
 static void ifup(struct net_device *net)
 {
 	int status;
@@ -1448,6 +1481,10 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn)
 
 	ifup(net);
 
+	if ((status = add_peer_route(dev, net, &dev->addr,
+						conn->hcon->dst_type)) < 0)
+		BT_INFO("Setting route failed %d", status);
+
 	return 0;
 }
 
-- 
1.7.11.7


^ permalink raw reply related

* [RFC v2 5/7] route: Exporting ip6_route_add() so that Bluetooth 6LoWPAN can use it
From: Jukka Rissanen @ 2013-10-30  9:00 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1383123661-15087-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 net/ipv6/route.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/ipv6/route.c b/net/ipv6/route.c
index c979dd9..d7c200f 100644
--- a/net/ipv6/route.c
+++ b/net/ipv6/route.c
@@ -1662,6 +1662,7 @@ out:
 		dst_free(&rt->dst);
 	return err;
 }
+EXPORT_SYMBOL_GPL(ip6_route_add);
 
 static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info)
 {
-- 
1.7.11.7


^ permalink raw reply related

* [RFC v2 4/7] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Jukka Rissanen @ 2013-10-30  9:00 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1383123661-15087-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 net/bluetooth/6lowpan.c | 1128 ++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1127 insertions(+), 1 deletion(-)

diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index aff478b..1cba7fd 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -11,6 +11,20 @@
    GNU General Public License for more details.
 */
 
+/*
+ * The compression, uncompression and IPv6 packet fragmentation code
+ * is from net/ieee802154/6lowpan.c with these copyrights
+ *
+ * Copyright 2011, Siemens AG
+ * written by Alexander Smirnov <alex.bluesman.smirnov@gmail.com>
+ *
+ * Based on patches from Jon Smirl <jonsmirl@gmail.com>
+ * Copyright (c) 2011 Jon Smirl <jonsmirl@gmail.com>
+ *
+ * Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ */
+
 #include <linux/version.h>
 #include <linux/bitops.h>
 #include <linux/if_arp.h>
@@ -69,6 +83,9 @@ struct lowpan_fragment {
 	struct list_head	list;		/* fragments list */
 };
 
+static LIST_HEAD(lowpan_fragments);
+static DEFINE_SPINLOCK(flist_lock);
+
 #define DELETE_TIMEOUT msecs_to_jiffies(1)
 
 /* TTL uncompression values */
@@ -110,8 +127,723 @@ static inline void raw_dump_table(const char *caller, char *msg,
 #endif /* DEBUG */
 }
 
+static inline bool fetch_skb(struct sk_buff *skb,
+					void *data, const unsigned int len)
+{
+	if (unlikely(!pskb_may_pull(skb, len)))
+		return true;
+
+	skb_copy_from_linear_data(skb, data, len);
+	skb_pull(skb, len);
+
+	return false;
+}
+
+/*
+ * Uncompress address function for source and
+ * destination address(non-multicast).
+ *
+ * address_mode is sam value or dam value.
+ */
+static int uncompress_addr(struct sk_buff *skb,
+				struct in6_addr *ipaddr,
+				const u8 address_mode,
+				const u8 *lladdr)
+{
+	bool fail;
+
+	switch (address_mode) {
+	case LOWPAN_IPHC_ADDR_00:
+		/* for global link addresses */
+		fail = fetch_skb(skb, ipaddr->s6_addr, 16);
+		break;
+	case LOWPAN_IPHC_ADDR_01:
+		/* fe:80::XXXX:XXXX:XXXX:XXXX */
+		ipaddr->s6_addr[0] = 0xFE;
+		ipaddr->s6_addr[1] = 0x80;
+		fail = fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+		break;
+	case LOWPAN_IPHC_ADDR_02:
+		/* fe:80::ff:fe00:XXXX */
+		ipaddr->s6_addr[0] = 0xFE;
+		ipaddr->s6_addr[1] = 0x80;
+		ipaddr->s6_addr[11] = 0xFF;
+		ipaddr->s6_addr[12] = 0xFE;
+		fail = fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+		break;
+	case LOWPAN_IPHC_ADDR_03:
+		fail = false;
+		/* XXX: support only normal addr (IEEE802154_ADDR_LONG) atm */
+
+		/* fe:80::XXXX:XXXX:XXXX:XXXX
+		 *        \_________________/
+		 *              hwaddr
+		 */
+		ipaddr->s6_addr[0] = 0xFE;
+		ipaddr->s6_addr[1] = 0x80;
+		memcpy(&ipaddr->s6_addr[8], lladdr, IEEE802154_ADDR_LEN);
+		break;
+	default:
+		pr_debug("Invalid address mode value: 0x%x\n", address_mode);
+		return -EINVAL;
+	}
+
+	if (fail) {
+		pr_debug("Failed to fetch skb data\n");
+		return -EIO;
+	}
+
+	raw_dump_inline(NULL, "Reconstructed ipv6 addr is",
+			ipaddr->s6_addr, 16);
+
+	return 0;
+}
+
+/*
+ * Uncompress address function for source context
+ * based address(non-multicast).
+ */
+static int uncompress_context_based_src_addr(struct sk_buff *skb,
+						struct in6_addr *ipaddr,
+						const u8 sam)
+{
+	switch (sam) {
+	case LOWPAN_IPHC_ADDR_00:
+		/* unspec address ::
+		 * Do nothing, address is already ::
+		 */
+		break;
+	case LOWPAN_IPHC_ADDR_01:
+		/* TODO */
+	case LOWPAN_IPHC_ADDR_02:
+		/* TODO */
+	case LOWPAN_IPHC_ADDR_03:
+		/* TODO */
+		netdev_warn(skb->dev, "SAM value 0x%x not supported\n", sam);
+		return -EINVAL;
+	default:
+		pr_debug("Invalid sam value: 0x%x\n", sam);
+		return -EINVAL;
+	}
+
+	raw_dump_inline(NULL,
+			"Reconstructed context based ipv6 src addr is",
+			ipaddr->s6_addr, 16);
+
+	return 0;
+}
+
+/*
+ * This func is called when the packet from BT LE device
+ * needs to be sent upper layers.
+ */
+static int give_skb_to_upper(struct sk_buff *skb,
+					struct net_device *dev)
+{
+	struct sk_buff *skb_cp;
+	int ret = NET_RX_SUCCESS;
+
+	skb_cp = skb_copy(skb, GFP_ATOMIC);
+	if (!skb_cp) {
+		ret = -ENOMEM;
+	} else {
+		ret = netif_rx(skb_cp);
+
+		BT_DBG("receive skb %d", ret);
+		if (ret < 0)
+			ret = NET_RX_DROP;
+	}
+
+	return ret;
+}
+
+static inline int fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+	if (unlikely(!pskb_may_pull(skb, 1)))
+		return -EINVAL;
+
+	*val = skb->data[0];
+	skb_pull(skb, 1);
+
+	return 0;
+}
+
+static inline int fetch_skb_u16(struct sk_buff *skb, u16 *val)
+{
+	if (unlikely(!pskb_may_pull(skb, 2)))
+		return -EINVAL;
+
+	*val = (skb->data[0] << 8) | skb->data[1];
+	skb_pull(skb, 2);
+
+	return 0;
+}
+
+static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
+				struct net_device *dev)
+{
+	struct sk_buff *new;
+	int stat;
+
+	new = skb_copy_expand(skb, sizeof(struct ipv6hdr), skb_tailroom(skb),
+								GFP_ATOMIC);
+	kfree_skb(skb);
+
+	if (!new)
+		return -ENOMEM;
+
+	skb_push(new, sizeof(struct ipv6hdr));
+	skb_reset_network_header(new);
+	skb_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
+
+	new->protocol = htons(ETH_P_IPV6);
+	new->pkt_type = PACKET_HOST;
+	new->dev = dev;
+
+	raw_dump_table(__func__,
+				"raw skb data dump before receiving",
+				new->data, new->len);
+
+	stat = give_skb_to_upper(new, dev);
+
+	kfree_skb(new);
+
+	return stat;
+}
+
+static void lowpan_fragment_timer_expired(unsigned long entry_addr)
+{
+	struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr;
+
+	pr_debug("timer expired for frame with tag %d\n", entry->tag);
+
+	list_del(&entry->list);
+	dev_kfree_skb(entry->skb);
+	kfree(entry);
+}
+
+static struct lowpan_fragment *alloc_new_frame(struct sk_buff *skb,
+							u16 len, u16 tag)
+{
+	struct lowpan_fragment *frame;
+
+	frame = kzalloc(sizeof(struct lowpan_fragment),
+			GFP_ATOMIC);
+	if (!frame)
+		goto frame_err;
+
+	INIT_LIST_HEAD(&frame->list);
+
+	frame->length = len;
+	frame->tag = tag;
+
+	/* allocate buffer for frame assembling */
+	frame->skb = netdev_alloc_skb_ip_align(skb->dev, frame->length +
+					       sizeof(struct ipv6hdr));
+
+	if (!frame->skb)
+		goto skb_err;
+
+	frame->skb->priority = skb->priority;
+	frame->skb->dev = skb->dev;
+
+	/* reserve headroom for uncompressed ipv6 header */
+	skb_reserve(frame->skb, sizeof(struct ipv6hdr));
+	skb_put(frame->skb, frame->length);
+
+	/* copy the first control block to keep a
+	 * trace of the link-layer addresses in case
+	 * of a link-local compressed address
+	 */
+	memcpy(frame->skb->cb, skb->cb, sizeof(skb->cb));
+
+	init_timer(&frame->timer);
+	/* time out is the same as for ipv6 - 60 sec */
+	frame->timer.expires = jiffies + LOWPAN_FRAG_TIMEOUT;
+	frame->timer.data = (unsigned long)frame;
+	frame->timer.function = lowpan_fragment_timer_expired;
+
+	add_timer(&frame->timer);
+
+	list_add_tail(&frame->list, &lowpan_fragments);
+
+	return frame;
+
+skb_err:
+	kfree(frame);
+frame_err:
+	return NULL;
+}
+
+/* Uncompress function for multicast destination address,
+ * when M bit is set.
+ */
+static int
+lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
+		struct in6_addr *ipaddr,
+		const u8 dam)
+{
+	bool fail;
+
+	switch (dam) {
+	case LOWPAN_IPHC_DAM_00:
+		/* 00:  128 bits.  The full address
+		 * is carried in-line.
+		 */
+		fail = fetch_skb(skb, ipaddr->s6_addr, 16);
+		break;
+	case LOWPAN_IPHC_DAM_01:
+		/* 01:  48 bits.  The address takes
+		 * the form ffXX::00XX:XXXX:XXXX.
+		 */
+		ipaddr->s6_addr[0] = 0xFF;
+		fail = fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+		fail |= fetch_skb(skb, &ipaddr->s6_addr[11], 5);
+		break;
+	case LOWPAN_IPHC_DAM_10:
+		/* 10:  32 bits.  The address takes
+		 * the form ffXX::00XX:XXXX.
+		 */
+		ipaddr->s6_addr[0] = 0xFF;
+		fail = fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+		fail |= fetch_skb(skb, &ipaddr->s6_addr[13], 3);
+		break;
+	case LOWPAN_IPHC_DAM_11:
+		/* 11:  8 bits.  The address takes
+		 * the form ff02::00XX.
+		 */
+		ipaddr->s6_addr[0] = 0xFF;
+		ipaddr->s6_addr[1] = 0x02;
+		fail = fetch_skb(skb, &ipaddr->s6_addr[15], 1);
+		break;
+	default:
+		pr_debug("DAM value has a wrong value: 0x%x\n", dam);
+		return -EINVAL;
+	}
+
+	if (fail) {
+		pr_debug("Failed to fetch skb data\n");
+		return -EIO;
+	}
+
+	raw_dump_inline(NULL,
+				"Reconstructed ipv6 multicast addr is",
+				ipaddr->s6_addr, 16);
+
+	return 0;
+}
+
+static int
+uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+	u8 tmp;
+
+	if (!uh)
+		goto err;
+
+	if (fetch_skb_u8(skb, &tmp))
+		goto err;
+
+	if ((tmp & LOWPAN_NHC_UDP_MASK) == LOWPAN_NHC_UDP_ID) {
+		pr_debug("UDP header uncompression\n");
+		switch (tmp & LOWPAN_NHC_UDP_CS_P_11) {
+		case LOWPAN_NHC_UDP_CS_P_00:
+			memcpy(&uh->source, &skb->data[0], 2);
+			memcpy(&uh->dest, &skb->data[2], 2);
+			skb_pull(skb, 4);
+			break;
+		case LOWPAN_NHC_UDP_CS_P_01:
+			memcpy(&uh->source, &skb->data[0], 2);
+			uh->dest =
+			   skb->data[2] + LOWPAN_NHC_UDP_8BIT_PORT;
+			skb_pull(skb, 3);
+			break;
+		case LOWPAN_NHC_UDP_CS_P_10:
+			uh->source = skb->data[0] + LOWPAN_NHC_UDP_8BIT_PORT;
+			memcpy(&uh->dest, &skb->data[1], 2);
+			skb_pull(skb, 3);
+			break;
+		case LOWPAN_NHC_UDP_CS_P_11:
+			uh->source =
+			   LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] >> 4);
+			uh->dest =
+			   LOWPAN_NHC_UDP_4BIT_PORT + (skb->data[0] & 0x0f);
+			skb_pull(skb, 1);
+			break;
+		default:
+			pr_debug("ERROR: unknown UDP format\n");
+			goto err;
+			break;
+		}
+
+		pr_debug("uncompressed UDP ports: src = %d, dst = %d\n",
+			 uh->source, uh->dest);
+
+		/* copy checksum */
+		memcpy(&uh->check, &skb->data[0], 2);
+		skb_pull(skb, 2);
+
+		/*
+		 * UDP lenght needs to be infered from the lower layers
+		 * here, we obtain the hint from the remaining size of the
+		 * frame
+		 */
+		uh->len = htons(skb->len + sizeof(struct udphdr));
+		pr_debug("uncompressed UDP length: src = %d", uh->len);
+	} else {
+		pr_debug("ERROR: unsupported NH format\n");
+		goto err;
+	}
+
+	return 0;
+err:
+	return -EINVAL;
+}
+
+static int process_data(struct sk_buff *skb, struct net_device *dev)
+{
+	struct ipv6hdr hdr = {};
+	u8 tmp, iphc0, iphc1, num_context = 0;
+	const u8 *_saddr = NULL, *_daddr = NULL;
+	struct lowpan_info *info;
+	int err;
+
+	raw_dump_table(__func__, "raw skb data dump uncompressed",
+				skb->data, skb->len);
+
+	/* at least two bytes will be used for the encoding */
+	if (skb->len < 2)
+		goto drop;
+
+	if (fetch_skb_u8(skb, &iphc0))
+		goto drop;
+
+	info = lowpan_info(dev);
+
+	/* fragments assembling */
+	switch (iphc0 & LOWPAN_DISPATCH_MASK) {
+	case LOWPAN_DISPATCH_FRAG1:
+	case LOWPAN_DISPATCH_FRAGN:
+	{
+		struct lowpan_fragment *frame;
+		/* slen stores the rightmost 8 bits of the 11 bits length */
+		u8 slen, offset = 0;
+		u16 len, tag;
+		bool found = false;
+
+		if (fetch_skb_u8(skb, &slen) || /* frame length */
+		    fetch_skb_u16(skb, &tag))  /* fragment tag */
+			goto drop;
+
+		/* adds the 3 MSB to the 8 LSB to retrieve the 11 bits length */
+		len = ((iphc0 & 7) << 8) | slen;
+
+		if ((iphc0 & LOWPAN_DISPATCH_MASK) == LOWPAN_DISPATCH_FRAG1) {
+			pr_debug("%s received a FRAG1 packet (tag: %d, "
+				 "size of the entire IP packet: %d)\n",
+				 __func__, tag, len);
+		} else { /* FRAGN */
+			if (fetch_skb_u8(skb, &offset))
+				goto unlock_and_drop;
+			pr_debug("%s received a FRAGN packet (tag: %d, "
+				 "size of the entire IP packet: %d, "
+				 "offset: %d)\n", __func__, tag, len,
+				offset * 8);
+		}
+
+		/*
+		 * check if frame assembling with the same tag is
+		 * already in progress
+		 */
+		spin_lock_bh(&flist_lock);
+
+		list_for_each_entry(frame, &lowpan_fragments, list)
+			if (frame->tag == tag) {
+				found = true;
+				break;
+			}
+
+		/* alloc new frame structure */
+		if (!found) {
+			pr_debug("%s first fragment received for tag %d, "
+				 "begin packet reassembly\n", __func__, tag);
+			frame = alloc_new_frame(skb, len, tag);
+			if (!frame)
+				goto unlock_and_drop;
+		}
+
+		/* if payload fits buffer, copy it */
+		if (likely((offset * 8 + skb->len) <= frame->length))
+			skb_copy_to_linear_data_offset(frame->skb, offset * 8,
+							skb->data, skb->len);
+		else
+			goto unlock_and_drop;
+
+		frame->bytes_rcv += skb->len;
+
+		/* frame assembling complete */
+		if ((frame->bytes_rcv == frame->length) &&
+		     frame->timer.expires > jiffies) {
+			/* if timer haven't expired - first of all delete it */
+			del_timer_sync(&frame->timer);
+			list_del(&frame->list);
+			spin_unlock_bh(&flist_lock);
+
+			pr_debug("%s successfully reassembled fragment "
+				 "(tag %d)\n", __func__, tag);
+
+			dev_kfree_skb(skb);
+			skb = frame->skb;
+			kfree(frame);
+
+			if (fetch_skb_u8(skb, &iphc0))
+				goto drop;
+
+			break;
+		}
+		spin_unlock_bh(&flist_lock);
+
+		return kfree_skb(skb), 0;
+	}
+	default:
+		break;
+	}
+
+	if (fetch_skb_u8(skb, &iphc1))
+		goto drop;
+
+	_saddr = info->ieee802154_addr;
+	_daddr = info->net->dev_addr;
+
+	/* another if the CID flag is set */
+	if (iphc1 & LOWPAN_IPHC_CID) {
+		pr_debug("CID flag is set, increase header with one\n");
+		if (fetch_skb_u8(skb, &num_context))
+			goto drop;
+	}
+
+	hdr.version = 6;
+
+	/* Traffic Class and Flow Label */
+	switch ((iphc0 & LOWPAN_IPHC_TF) >> 3) {
+	/*
+	 * Traffic Class and FLow Label carried in-line
+	 * ECN + DSCP + 4-bit Pad + Flow Label (4 bytes)
+	 */
+	case 0: /* 00b */
+		if (fetch_skb_u8(skb, &tmp))
+			goto drop;
+
+		memcpy(&hdr.flow_lbl, &skb->data[0], 3);
+		skb_pull(skb, 3);
+		hdr.priority = ((tmp >> 2) & 0x0f);
+		hdr.flow_lbl[0] = ((tmp >> 2) & 0x30) | (tmp << 6) |
+					(hdr.flow_lbl[0] & 0x0f);
+		break;
+	/*
+	 * Traffic class carried in-line
+	 * ECN + DSCP (1 byte), Flow Label is elided
+	 */
+	case 1: /* 10b */
+		if (fetch_skb_u8(skb, &tmp))
+			goto drop;
+
+		hdr.priority = ((tmp >> 2) & 0x0f);
+		hdr.flow_lbl[0] = ((tmp << 6) & 0xC0) | ((tmp >> 2) & 0x30);
+		break;
+	/*
+	 * Flow Label carried in-line
+	 * ECN + 2-bit Pad + Flow Label (3 bytes), DSCP is elided
+	 */
+	case 2: /* 01b */
+		if (fetch_skb_u8(skb, &tmp))
+			goto drop;
+
+		hdr.flow_lbl[0] = (skb->data[0] & 0x0F) | ((tmp >> 2) & 0x30);
+		memcpy(&hdr.flow_lbl[1], &skb->data[0], 2);
+		skb_pull(skb, 2);
+		break;
+	/* Traffic Class and Flow Label are elided */
+	case 3: /* 11b */
+		break;
+	default:
+		break;
+	}
+
+	/* Next Header */
+	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+		/* Next header is carried inline */
+		if (fetch_skb_u8(skb, &(hdr.nexthdr)))
+			goto drop;
+
+		pr_debug("NH flag is set, next header carried inline: %02x\n",
+			 hdr.nexthdr);
+	}
+
+	/* Hop Limit */
+	if ((iphc0 & 0x03) != LOWPAN_IPHC_TTL_I)
+		hdr.hop_limit = lowpan_ttl_values[iphc0 & 0x03];
+	else {
+		if (fetch_skb_u8(skb, &(hdr.hop_limit)))
+			goto drop;
+	}
+
+	/* Extract SAM to the tmp variable */
+	tmp = ((iphc1 & LOWPAN_IPHC_SAM) >> LOWPAN_IPHC_SAM_BIT) & 0x03;
+
+	if (iphc1 & LOWPAN_IPHC_SAC) {
+		/* Source address context based uncompression */
+		pr_debug("SAC bit is set. Handle context based source address.\n");
+		err = uncompress_context_based_src_addr(
+				skb, &hdr.saddr, tmp);
+	} else {
+		/* Source address uncompression */
+		pr_debug("source address stateless compression\n");
+		err = uncompress_addr(skb, &hdr.saddr, tmp, _saddr);
+	}
+
+	/* Check on error of previous branch */
+	if (err)
+		goto drop;
+
+	/* Extract DAM to the tmp variable */
+	tmp = ((iphc1 & LOWPAN_IPHC_DAM_11) >> LOWPAN_IPHC_DAM_BIT) & 0x03;
+
+	/* check for Multicast Compression */
+	if (iphc1 & LOWPAN_IPHC_M) {
+		if (iphc1 & LOWPAN_IPHC_DAC) {
+			pr_debug("dest: context-based mcast compression\n");
+			/* TODO: implement this */
+		} else {
+			err = lowpan_uncompress_multicast_daddr(
+						skb, &hdr.daddr, tmp);
+			if (err)
+				goto drop;
+		}
+	} else {
+		pr_debug("dest: stateless compression\n");
+		err = uncompress_addr(skb, &hdr.daddr, tmp, _daddr);
+		if (err)
+			goto drop;
+	}
+
+	/* UDP data uncompression */
+	if (iphc0 & LOWPAN_IPHC_NH_C) {
+		struct udphdr uh;
+		struct sk_buff *new;
+		if (uncompress_udp_header(skb, &uh))
+			goto drop;
+
+		/*
+		 * replace the compressed UDP head by the uncompressed UDP
+		 * header
+		 */
+		new = skb_copy_expand(skb, sizeof(struct udphdr),
+				      skb_tailroom(skb), GFP_ATOMIC);
+		kfree_skb(skb);
+
+		if (!new)
+			return -ENOMEM;
+
+		skb = new;
+
+		skb_push(skb, sizeof(struct udphdr));
+		skb_reset_transport_header(skb);
+		skb_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
+
+		raw_dump_table(__func__, "raw UDP header dump",
+				      (u8 *)&uh, sizeof(uh));
+
+		hdr.nexthdr = UIP_PROTO_UDP;
+	}
+
+	/* Not fragmented package */
+	hdr.payload_len = htons(skb->len);
+
+	pr_debug("skb headroom size = %d, data length = %d\n",
+		 skb_headroom(skb), skb->len);
+
+	pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n\t"
+		 "nexthdr = 0x%02x\n\thop_lim = %d\n", hdr.version,
+		 ntohs(hdr.payload_len), hdr.nexthdr, hdr.hop_limit);
+
+	raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+							sizeof(hdr));
+
+	return skb_deliver(skb, &hdr, dev);
+
+unlock_and_drop:
+	spin_unlock_bh(&flist_lock);
+drop:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
 static int recv_pkt(struct sk_buff *skb, struct net_device *dev)
 {
+	struct sk_buff *local_skb;
+
+	if (!netif_running(dev))
+		goto drop;
+
+	if (dev->type != ARPHRD_RAWIP)
+		goto drop;
+
+	raw_dump_table(__func__, "raw recv dump", skb->head,
+							skb->len);
+
+	/* check that it's our buffer */
+	if (skb->data[0] == LOWPAN_DISPATCH_IPV6) {
+		/* Copy the packet so that the IPv6 header is
+		 * properly aligned.
+		 */
+		local_skb = skb_copy_expand(skb, NET_SKB_PAD - 1,
+					    skb_tailroom(skb), GFP_ATOMIC);
+		if (!local_skb)
+			goto drop;
+
+		local_skb->protocol = htons(ETH_P_IPV6);
+		local_skb->pkt_type = PACKET_HOST;
+
+		skb_reset_network_header(local_skb);
+		skb_set_transport_header(local_skb, sizeof(struct ipv6hdr));
+
+		if (give_skb_to_upper(local_skb,
+						dev) != NET_RX_SUCCESS) {
+			kfree_skb(local_skb);
+			goto drop;
+		}
+
+		dev->stats.rx_bytes += skb->len;
+		dev->stats.rx_packets++;
+
+		kfree_skb(local_skb);
+		kfree_skb(skb);
+	} else {
+		switch (skb->data[0] & 0xe0) {
+		case LOWPAN_DISPATCH_IPHC:	/* ipv6 datagram */
+		case LOWPAN_DISPATCH_FRAG1:	/* first fragment header */
+		case LOWPAN_DISPATCH_FRAGN:	/* next fragments headers */
+			local_skb = skb_clone(skb, GFP_ATOMIC);
+			if (!local_skb)
+				goto drop;
+			if (process_data(local_skb,
+						dev) != NET_RX_SUCCESS)
+				goto drop;
+
+			dev->stats.rx_bytes += skb->len;
+			dev->stats.rx_packets++;
+
+			kfree_skb(skb);
+			break;
+		default:
+			break;
+		}
+	}
+
+	return NET_RX_SUCCESS;
+
+drop:
 	kfree_skb(skb);
 	return NET_RX_DROP;
 }
@@ -147,19 +879,408 @@ static void do_send(struct l2cap_conn *conn, struct sk_buff *skb)
 	BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len,
 	       skb->priority);
 
+	hci_send_acl(conn->hchan, skb, ACL_START);
 	return;
 }
 
+static inline int skbuff_copy(void *msg, int len,
+					int count, int mtu,
+					struct sk_buff *skb,
+					struct net_device *dev)
+{
+	struct sk_buff **frag;
+	int sent = 0;
+
+	memcpy(skb_put(skb, count), msg, count);
+
+	sent += count;
+	msg  += count;
+	len  -= count;
+
+	dev->stats.tx_bytes += count;
+	dev->stats.tx_packets++;
+
+	raw_dump_table(__func__, "Sending", skb->data, skb->len);
+
+	/* Continuation fragments (no L2CAP header) */
+	frag = &skb_shinfo(skb)->frag_list;
+	while (len > 0) {
+		struct sk_buff *tmp;
+
+		count = min_t(unsigned int, mtu, len);
+
+		tmp = bt_skb_alloc(count, GFP_KERNEL);
+		if (IS_ERR(tmp))
+			return PTR_ERR(tmp);
+
+		*frag = tmp;
+
+		memcpy(skb_put(*frag, count), msg, count);
+
+		raw_dump_table(__func__, "Sending fragment",
+					(*frag)->data, count);
+
+		(*frag)->priority = skb->priority;
+
+		sent += count;
+		msg  += count;
+		len  -= count;
+
+		skb->len += (*frag)->len;
+		skb->data_len += (*frag)->len;
+
+		frag = &(*frag)->next;
+
+		dev->stats.tx_bytes += count;
+		dev->stats.tx_packets++;
+	}
+
+	return sent;
+}
+
+static struct sk_buff *create_pdu(struct l2cap_conn *conn,
+					void *msg, size_t len,
+					u32 priority,
+					struct net_device *dev)
+{
+	struct sk_buff *skb;
+	int err, count;
+	struct l2cap_hdr *lh;
+
+	if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
+		/* XXX: This should be not needed and atm is only used for
+		 * testing purposes */
+		conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE;
+
+	count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len);
+
+	BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count);
+
+	skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_KERNEL);
+	if (IS_ERR(skb))
+		return skb;
+
+	skb->priority = priority;
+
+	lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE);
+	lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN);
+	lh->len = cpu_to_le16(len);
+
+	err = skbuff_copy(msg, len, count, conn->mtu, skb, dev);
+	if (unlikely(err < 0)) {
+		kfree_skb(skb);
+		BT_DBG("skbuff copy %d failed", err);
+		return ERR_PTR(err);
+	}
+
+	return skb;
+}
+
 static int conn_send(struct l2cap_conn *conn,
 			void *msg, size_t len, u32 priority,
 			struct net_device *dev)
 {
-	struct sk_buff *skb = {0};
+	struct sk_buff *skb;
+
+	skb = create_pdu(conn, msg, len, priority, dev);
+	if (IS_ERR(skb))
+		return -EINVAL;
 
 	do_send(conn, skb);
 	return 0;
 }
 
+static u8 lowpan_compress_addr_64(u8 **hc06_ptr, u8 shift,
+				const struct in6_addr *ipaddr,
+				const unsigned char *lladdr)
+{
+	u8 val = 0;
+
+	if (is_addr_mac_addr_based(ipaddr, lladdr))
+		val = 3; /* 0-bits */
+	else if (lowpan_is_iid_16_bit_compressable(ipaddr)) {
+		/* compress IID to 16 bits xxxx::XXXX */
+		memcpy(*hc06_ptr, &ipaddr->s6_addr16[7], 2);
+		*hc06_ptr += 2;
+		val = 2; /* 16-bits */
+	} else {
+		/* do not compress IID => xxxx::IID */
+		memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
+		*hc06_ptr += 8;
+		val = 1; /* 64-bits */
+	}
+
+	return rol8(val, shift);
+}
+
+static void compress_udp_header(u8 **hc06_ptr, struct sk_buff *skb)
+{
+	struct udphdr *uh = udp_hdr(skb);
+
+	if (((uh->source & LOWPAN_NHC_UDP_4BIT_MASK) ==
+				LOWPAN_NHC_UDP_4BIT_PORT) &&
+	    ((uh->dest & LOWPAN_NHC_UDP_4BIT_MASK) ==
+				LOWPAN_NHC_UDP_4BIT_PORT)) {
+		pr_debug("UDP header: both ports compression to 4 bits\n");
+		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_11;
+		**(hc06_ptr + 1) = /* subtraction is faster */
+		   (u8)((uh->dest - LOWPAN_NHC_UDP_4BIT_PORT) +
+		       ((uh->source & LOWPAN_NHC_UDP_4BIT_PORT) << 4));
+		*hc06_ptr += 2;
+	} else if ((uh->dest & LOWPAN_NHC_UDP_8BIT_MASK) ==
+			LOWPAN_NHC_UDP_8BIT_PORT) {
+		pr_debug("UDP header: remove 8 bits of dest\n");
+		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_01;
+		memcpy(*hc06_ptr + 1, &uh->source, 2);
+		**(hc06_ptr + 3) = (u8)(uh->dest - LOWPAN_NHC_UDP_8BIT_PORT);
+		*hc06_ptr += 4;
+	} else if ((uh->source & LOWPAN_NHC_UDP_8BIT_MASK) ==
+			LOWPAN_NHC_UDP_8BIT_PORT) {
+		pr_debug("UDP header: remove 8 bits of source\n");
+		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_10;
+		memcpy(*hc06_ptr + 1, &uh->dest, 2);
+		**(hc06_ptr + 3) = (u8)(uh->source - LOWPAN_NHC_UDP_8BIT_PORT);
+		*hc06_ptr += 4;
+	} else {
+		pr_debug("UDP header: can't compress\n");
+		**hc06_ptr = LOWPAN_NHC_UDP_CS_P_00;
+		memcpy(*hc06_ptr + 1, &uh->source, 2);
+		memcpy(*hc06_ptr + 3, &uh->dest, 2);
+		*hc06_ptr += 5;
+	}
+
+	/* checksum is always inline */
+	memcpy(*hc06_ptr, &uh->check, 2);
+	*hc06_ptr += 2;
+
+	/* skip the UDP header */
+	skb_pull(skb, sizeof(struct udphdr));
+}
+
+static inline int lowpan_fetch_skb_u8(struct sk_buff *skb, u8 *val)
+{
+	if (unlikely(!pskb_may_pull(skb, 1)))
+		return -EINVAL;
+
+	*val = skb->data[0];
+	skb_pull(skb, 1);
+
+	return 0;
+}
+
+static inline int lowpan_fetch_skb_u16(struct sk_buff *skb, u16 *val)
+{
+	if (unlikely(!pskb_may_pull(skb, 2)))
+		return -EINVAL;
+
+	*val = (skb->data[0] << 8) | skb->data[1];
+	skb_pull(skb, 2);
+
+	return 0;
+}
+
+static int header_create(struct sk_buff *skb, struct net_device *dev,
+			unsigned short type, const void *_daddr,
+			const void *_saddr, unsigned int len)
+{
+	u8 tmp, iphc0, iphc1, *hc06_ptr;
+	struct ipv6hdr *hdr;
+	u8 *saddr, *daddr;
+	u8 head[100];
+	struct lowpan_info *info;
+
+	if (type != ETH_P_IPV6)
+		return -EINVAL;
+
+	info = lowpan_info(dev);
+	daddr = info->ieee802154_addr;
+	saddr = info->net->dev_addr;
+
+	hdr = ipv6_hdr(skb);
+	hc06_ptr = head + 2;
+
+	pr_debug("IPv6 header dump:\n\tversion = %d\n\tlength  = %d\n"
+		 "\tnexthdr = 0x%02x\n\thop_lim = %d\n", hdr->version,
+		 ntohs(hdr->payload_len), hdr->nexthdr, hdr->hop_limit);
+
+	raw_dump_table(__func__, "raw skb network header dump",
+		skb_network_header(skb), sizeof(struct ipv6hdr));
+
+	raw_dump_inline(__func__, "saddr",
+			(unsigned char *)saddr, 8);
+
+	/*
+	 * As we copy some bit-length fields, in the IPHC encoding bytes,
+	 * we sometimes use |=
+	 * If the field is 0, and the current bit value in memory is 1,
+	 * this does not work. We therefore reset the IPHC encoding here
+	 */
+	iphc0 = LOWPAN_DISPATCH_IPHC;
+	iphc1 = 0;
+
+	/* TODO: context lookup */
+
+	raw_dump_inline(__func__, "daddr",
+			(unsigned char *)daddr, 8);
+
+	raw_dump_table(__func__,
+				"sending raw skb network uncompressed packet",
+				skb->data, skb->len);
+
+	/*
+	 * Traffic class, flow label
+	 * If flow label is 0, compress it. If traffic class is 0, compress it
+	 * We have to process both in the same time as the offset of traffic
+	 * class depends on the presence of version and flow label
+	 */
+
+	/* hc06 format of TC is ECN | DSCP , original one is DSCP | ECN */
+	tmp = (hdr->priority << 4) | (hdr->flow_lbl[0] >> 4);
+	tmp = ((tmp & 0x03) << 6) | (tmp >> 2);
+
+	if (((hdr->flow_lbl[0] & 0x0F) == 0) &&
+	     (hdr->flow_lbl[1] == 0) && (hdr->flow_lbl[2] == 0)) {
+		/* flow label can be compressed */
+		iphc0 |= LOWPAN_IPHC_FL_C;
+		if ((hdr->priority == 0) &&
+		   ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+			/* compress (elide) all */
+			iphc0 |= LOWPAN_IPHC_TC_C;
+		} else {
+			/* compress only the flow label */
+			*hc06_ptr = tmp;
+			hc06_ptr += 1;
+		}
+	} else {
+		/* Flow label cannot be compressed */
+		if ((hdr->priority == 0) &&
+		   ((hdr->flow_lbl[0] & 0xF0) == 0)) {
+			/* compress only traffic class */
+			iphc0 |= LOWPAN_IPHC_TC_C;
+			*hc06_ptr = (tmp & 0xc0) | (hdr->flow_lbl[0] & 0x0F);
+			memcpy(hc06_ptr + 1, &hdr->flow_lbl[1], 2);
+			hc06_ptr += 3;
+		} else {
+			/* compress nothing */
+			memcpy(hc06_ptr, &hdr, 4);
+			/* replace the top byte with new ECN | DSCP format */
+			*hc06_ptr = tmp;
+			hc06_ptr += 4;
+		}
+	}
+
+	/* NOTE: payload length is always compressed */
+
+	/* Next Header is compress if UDP */
+	if (hdr->nexthdr == UIP_PROTO_UDP)
+		iphc0 |= LOWPAN_IPHC_NH_C;
+
+	if ((iphc0 & LOWPAN_IPHC_NH_C) == 0) {
+		*hc06_ptr = hdr->nexthdr;
+		hc06_ptr += 1;
+	}
+
+	/*
+	 * Hop limit
+	 * if 1:   compress, encoding is 01
+	 * if 64:  compress, encoding is 10
+	 * if 255: compress, encoding is 11
+	 * else do not compress
+	 */
+	switch (hdr->hop_limit) {
+	case 1:
+		iphc0 |= LOWPAN_IPHC_TTL_1;
+		break;
+	case 64:
+		iphc0 |= LOWPAN_IPHC_TTL_64;
+		break;
+	case 255:
+		iphc0 |= LOWPAN_IPHC_TTL_255;
+		break;
+	default:
+		*hc06_ptr = hdr->hop_limit;
+		hc06_ptr += 1;
+		break;
+	}
+
+	/* source address compression */
+	if (is_addr_unspecified(&hdr->saddr)) {
+		pr_debug("source address is unspecified, setting SAC\n");
+		iphc1 |= LOWPAN_IPHC_SAC;
+	/* TODO: context lookup */
+	} else if (is_addr_link_local(&hdr->saddr)) {
+		pr_debug("source address is link-local\n");
+		iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+				LOWPAN_IPHC_SAM_BIT, &hdr->saddr, saddr);
+	} else {
+		pr_debug("send the full source address\n");
+		memcpy(hc06_ptr, &hdr->saddr.s6_addr16[0], 16);
+		hc06_ptr += 16;
+	}
+
+	/* destination address compression */
+	if (is_addr_mcast(&hdr->daddr)) {
+		pr_debug("destination address is multicast: ");
+		iphc1 |= LOWPAN_IPHC_M;
+		if (lowpan_is_mcast_addr_compressable8(&hdr->daddr)) {
+			pr_debug("compressed to 1 octet\n");
+			iphc1 |= LOWPAN_IPHC_DAM_11;
+			/* use last byte */
+			*hc06_ptr = hdr->daddr.s6_addr[15];
+			hc06_ptr += 1;
+		} else if (lowpan_is_mcast_addr_compressable32(&hdr->daddr)) {
+			pr_debug("compressed to 4 octets\n");
+			iphc1 |= LOWPAN_IPHC_DAM_10;
+			/* second byte + the last three */
+			*hc06_ptr = hdr->daddr.s6_addr[1];
+			memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[13], 3);
+			hc06_ptr += 4;
+		} else if (lowpan_is_mcast_addr_compressable48(&hdr->daddr)) {
+			pr_debug("compressed to 6 octets\n");
+			iphc1 |= LOWPAN_IPHC_DAM_01;
+			/* second byte + the last five */
+			*hc06_ptr = hdr->daddr.s6_addr[1];
+			memcpy(hc06_ptr + 1, &hdr->daddr.s6_addr[11], 5);
+			hc06_ptr += 6;
+		} else {
+			pr_debug("using full address\n");
+			iphc1 |= LOWPAN_IPHC_DAM_00;
+			memcpy(hc06_ptr, &hdr->daddr.s6_addr[0], 16);
+			hc06_ptr += 16;
+		}
+	} else {
+		/* TODO: context lookup */
+		if (is_addr_link_local(&hdr->daddr)) {
+			pr_debug("dest address is unicast and link-local\n");
+			iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+				LOWPAN_IPHC_DAM_BIT, &hdr->daddr, daddr);
+		} else {
+			pr_debug("dest address is unicast: using full one\n");
+			memcpy(hc06_ptr, &hdr->daddr.s6_addr16[0], 16);
+			hc06_ptr += 16;
+		}
+	}
+
+	/* UDP header compression */
+	if (hdr->nexthdr == UIP_PROTO_UDP)
+		compress_udp_header(&hc06_ptr, skb);
+
+	head[0] = iphc0;
+	head[1] = iphc1;
+
+	skb_pull(skb, sizeof(struct ipv6hdr));
+
+	memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+
+	BT_DBG("header len %d skb %u", (int)(hc06_ptr - head), skb->len);
+
+	skb_reset_network_header(skb);
+
+	raw_dump_table(__func__, "raw skb data dump compressed",
+				skb->data, skb->len);
+	return 0;
+}
+
 /* Packet to BT LE device */
 static int send_pkt(struct l2cap_conn *conn, const void *saddr,
 			const void *daddr, struct sk_buff *skb,
@@ -198,6 +1319,10 @@ static const struct net_device_ops netdev_ops = {
 	.ndo_start_xmit		= xmit,
 };
 
+static struct header_ops header_ops = {
+	.create	= header_create,
+};
+
 static void setup(struct net_device *dev)
 {
 	dev->addr_len		= IEEE802154_ADDR_LEN;
@@ -211,6 +1336,7 @@ static void setup(struct net_device *dev)
 	dev->watchdog_timeo	= 0;
 
 	dev->netdev_ops		= &netdev_ops;
+	dev->header_ops		= &header_ops;
 	dev->destructor		= free_netdev;
 }
 
-- 
1.7.11.7


^ permalink raw reply related

* [RFC v2 3/7] Bluetooth: Initial skeleton code for BT 6LoWPAN
From: Jukka Rissanen @ 2013-10-30  9:00 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1383123661-15087-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 include/net/bluetooth/l2cap.h |   1 +
 net/bluetooth/6lowpan.c       | 401 ++++++++++++++++++++++++++++++++++++++++++
 net/bluetooth/6lowpan.h       |  26 +++
 net/bluetooth/Makefile        |   2 +-
 net/bluetooth/l2cap_core.c    |  22 ++-
 5 files changed, 450 insertions(+), 2 deletions(-)
 create mode 100644 net/bluetooth/6lowpan.c
 create mode 100644 net/bluetooth/6lowpan.h

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 5132990..c28ac0d 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -133,6 +133,7 @@ struct l2cap_conninfo {
 #define L2CAP_FC_L2CAP		0x02
 #define L2CAP_FC_CONNLESS	0x04
 #define L2CAP_FC_A2MP		0x08
+#define L2CAP_FC_6LOWPAN        0x3e
 
 /* L2CAP Control Field bit masks */
 #define L2CAP_CTRL_SAR			0xC000
diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
new file mode 100644
index 0000000..aff478b
--- /dev/null
+++ b/net/bluetooth/6lowpan.c
@@ -0,0 +1,401 @@
+/*
+   Copyright (c) 2013 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#include <linux/version.h>
+#include <linux/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <net/ipv6.h>
+#include <net/ip6_route.h>
+#include <net/addrconf.h>
+
+#include <net/af_ieee802154.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "../ieee802154/6lowpan.h" /* for the compression defines */
+
+#define IFACE_NAME_TEMPLATE "bt%d"
+
+/*
+ * The devices list contains those devices that we are acting
+ * as a proxy. The BT 6LoWPAN device is a virtual device that
+ * connects to the Bluetooth LE device. The real connection to
+ * BT device is done via l2cap layer. There exists one
+ * virtual device / one BT 6LoWPAN device. The list contains
+ * struct lowpan_dev elements.
+ */
+static LIST_HEAD(bt_6lowpan_devices);
+DEFINE_RWLOCK(net_dev_list_lock);
+
+struct lowpan_dev {
+	struct net_device *dev;
+	struct delayed_work delete_timer;
+	struct list_head list;
+};
+
+struct lowpan_info {
+	struct net_device *net;
+	struct l2cap_conn *conn;
+	uint16_t ifindex;
+	bdaddr_t myaddr;
+
+	/* peer addresses in various formats */
+	bdaddr_t addr;
+	unsigned char ieee802154_addr[IEEE802154_ADDR_LEN];
+	struct in6_addr peer;
+};
+
+struct lowpan_fragment {
+	struct sk_buff		*skb;		/* skb to be assembled */
+	u16			length;		/* length to be assemled */
+	u32			bytes_rcv;	/* bytes received */
+	u16			tag;		/* current fragment tag */
+	struct timer_list	timer;		/* assembling timer */
+	struct list_head	list;		/* fragments list */
+};
+
+#define DELETE_TIMEOUT msecs_to_jiffies(1)
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = {0, 1, 64, 255};
+
+static inline struct
+lowpan_info *lowpan_info(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+				   unsigned char *buf, int len)
+{
+#ifdef DEBUG
+	if (msg)
+		pr_debug("%s():%s: ", caller, msg);
+	print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_NONE,
+		       16, 1, buf, len, false);
+#endif /* DEBUG */
+}
+
+/*
+ * print data in a table format:
+ *
+ * addr: xx xx xx xx xx xx
+ * addr: xx xx xx xx xx xx
+ * ...
+ */
+static inline void raw_dump_table(const char *caller, char *msg,
+				   unsigned char *buf, int len)
+{
+#ifdef DEBUG
+	if (msg)
+		pr_debug("%s():%s:\n", caller, msg);
+	print_hex_dump(KERN_DEBUG, "\t", DUMP_PREFIX_OFFSET,
+		       16, 1, buf, len, false);
+#endif /* DEBUG */
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+/* Packet from BT LE device */
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct lowpan_dev *entry, *tmp;
+	struct net_device *dev = NULL;
+	int status = -ENOENT;
+
+	write_lock(&net_dev_list_lock);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		if (lowpan_info(entry->dev)->conn == conn) {
+			dev = lowpan_info(entry->dev)->net;
+			break;
+		}
+	}
+
+	write_unlock(&net_dev_list_lock);
+
+	if (dev) {
+		status = recv_pkt(skb, dev);
+		BT_DBG("recv pkt %d", status);
+	}
+
+	return status;
+}
+
+static void do_send(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	BT_DBG("conn %p, skb %p len %d priority %u", conn, skb, skb->len,
+	       skb->priority);
+
+	return;
+}
+
+static int conn_send(struct l2cap_conn *conn,
+			void *msg, size_t len, u32 priority,
+			struct net_device *dev)
+{
+	struct sk_buff *skb = {0};
+
+	do_send(conn, skb);
+	return 0;
+}
+
+/* Packet to BT LE device */
+static int send_pkt(struct l2cap_conn *conn, const void *saddr,
+			const void *daddr, struct sk_buff *skb,
+			struct net_device *dev)
+{
+	raw_dump_table(__func__, "raw skb data dump before fragmentation",
+				skb->data, skb->len);
+
+	return conn_send(conn, skb->data, skb->len, 0, dev);
+}
+
+static netdev_tx_t xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	int err = -1;
+
+	pr_debug("ble 6lowpan packet xmit\n");
+
+	if (lowpan_info(dev)->conn)
+		err = send_pkt(lowpan_info(dev)->conn,
+				dev->dev_addr,
+				&lowpan_info(dev)->ieee802154_addr,
+				skb,
+				dev);
+	else
+		BT_DBG("ERROR: no BT LE 6LoWPAN device found");
+
+	dev_kfree_skb(skb);
+
+	if (err)
+		BT_DBG("ERROR: xmit failed (%d)", err);
+
+	return (err < 0) ? NET_XMIT_DROP : err;
+}
+
+static const struct net_device_ops netdev_ops = {
+	.ndo_start_xmit		= xmit,
+};
+
+static void setup(struct net_device *dev)
+{
+	dev->addr_len		= IEEE802154_ADDR_LEN;
+	dev->type		= ARPHRD_RAWIP;
+
+	dev->hard_header_len	= 0;
+	dev->needed_tailroom	= 0;
+	dev->mtu		= IPV6_MIN_MTU;
+	dev->tx_queue_len	= 0;
+	dev->flags		= IFF_RUNNING | IFF_POINTOPOINT;
+	dev->watchdog_timeo	= 0;
+
+	dev->netdev_ops		= &netdev_ops;
+	dev->destructor		= free_netdev;
+}
+
+static struct device_type bt_type = {
+	.name	= "bluetooth",
+};
+
+static void set_addr(u8 *eui, u8 *addr, u8 addr_type)
+{
+	/* addr is the BT address in little-endian format */
+	eui[0] = addr[5];
+	eui[1] = addr[4];
+	eui[2] = addr[3];
+	eui[3] = 0xFF;
+	eui[4] = 0xFE;
+	eui[5] = addr[2];
+	eui[6] = addr[1];
+	eui[7] = addr[0];
+
+	eui[0] ^= 2;
+
+	if (addr_type == BDADDR_LE_PUBLIC)
+		eui[0] &= ~1;
+	else
+		eui[0] |= 1;
+}
+
+static void set_dev_addr(struct net_device *net, bdaddr_t *addr,
+			u8 addr_type)
+{
+	net->addr_assign_type = NET_ADDR_PERM;
+	set_addr(net->dev_addr, addr->b, addr_type);
+	net->dev_addr[0] ^= 2;
+}
+
+static void ifup(struct net_device *net)
+{
+	int status;
+
+	rtnl_lock();
+	if ((status = dev_open(net)) < 0)
+		BT_INFO("iface %s cannot be opened (%d)", net->name, status);
+	rtnl_unlock();
+}
+
+/*
+ * This gets called when BT LE 6LoWPAN device is connected. We then
+ * create network device that acts as a proxy between BT LE device
+ * and kernel network stack.
+ */
+int bt_6lowpan_add_conn(struct l2cap_conn *conn)
+{
+	struct net_device *net;
+	struct lowpan_info *dev;
+	struct lowpan_dev *entry;
+	struct inet6_dev *idev;
+	int status;
+
+	net = alloc_netdev(sizeof(struct lowpan_info), IFACE_NAME_TEMPLATE,
+								setup);
+	if (!net)
+		return -ENOMEM;
+
+	dev = netdev_priv(net);
+	dev->net = net;
+
+	memcpy(&dev->myaddr, &conn->hcon->src, sizeof(bdaddr_t));
+	memcpy(&dev->addr, &conn->hcon->dst, sizeof(bdaddr_t));
+
+	set_dev_addr(net, &dev->myaddr, conn->hcon->src_type);
+
+	dev->conn = conn;
+
+	net->netdev_ops = &netdev_ops;
+	SET_NETDEV_DEV(net, &conn->hcon->dev);
+	SET_NETDEV_DEVTYPE(net, &bt_type);
+
+	status = register_netdev(net);
+	if (status < 0) {
+		BT_INFO("register_netdev failed %d", status);
+		free_netdev(net);
+		return status;
+	}
+
+	BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
+		net->ifindex, &dev->addr, &dev->myaddr);
+	dev->ifindex = net->ifindex;
+	set_bit(__LINK_STATE_PRESENT, &net->state);
+
+	idev = in6_dev_get(net);
+	if (idev) {
+		idev->cnf.autoconf = 1;
+		idev->cnf.forwarding = 1;
+		idev->cnf.accept_ra = 2;
+
+		in6_dev_put(idev);
+	}
+
+	entry = kzalloc(sizeof(struct lowpan_dev), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	entry->dev = net;
+
+	write_lock(&net_dev_list_lock);
+	INIT_LIST_HEAD(&entry->list);
+	list_add(&entry->list, &bt_6lowpan_devices);
+	write_unlock(&net_dev_list_lock);
+
+	ifup(net);
+
+	return 0;
+}
+
+static void delete_timeout(struct work_struct *work)
+{
+	struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
+						delete_timer.work);
+
+	unregister_netdev(entry->dev);
+	kfree(entry);
+}
+
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+	struct lowpan_dev *entry, *tmp;
+	int status = -ENOENT;
+
+	write_lock(&net_dev_list_lock);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		if (lowpan_info(entry->dev)->conn == conn) {
+			list_del(&entry->list);
+			status = 0;
+			break;
+		}
+	}
+
+	write_unlock(&net_dev_list_lock);
+
+	if (!status) {
+		INIT_DELAYED_WORK(&entry->delete_timer, delete_timeout);
+		schedule_delayed_work(&entry->delete_timer, DELETE_TIMEOUT);
+	}
+
+	return status;
+}
+
+static int device_event(struct notifier_block *unused,
+				unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct lowpan_dev *entry, *tmp;
+
+	if (dev->type != ARPHRD_RAWIP)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+		write_lock(&net_dev_list_lock);
+		list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
+					list) {
+			if (entry->dev == dev) {
+				list_del(&entry->list);
+				kfree(entry);
+				break;
+			}
+		}
+		write_unlock(&net_dev_list_lock);
+		break;
+	}
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block bt_6lowpan_dev_notifier = {
+	.notifier_call = device_event,
+};
+
+int bt_6lowpan_init(void)
+{
+	return register_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
+
+void bt_6lowpan_cleanup(void)
+{
+	unregister_netdevice_notifier(&bt_6lowpan_dev_notifier);
+}
diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h
new file mode 100644
index 0000000..680eac8
--- /dev/null
+++ b/net/bluetooth/6lowpan.h
@@ -0,0 +1,26 @@
+/*
+   Copyright (c) 2013 Intel Corp.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License version 2 and
+   only version 2 as published by the Free Software Foundation.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+*/
+
+#ifndef __6LOWPAN_H
+#define __6LOWPAN_H
+
+#include <linux/skbuff.h>
+#include <net/bluetooth/l2cap.h>
+
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
+int bt_6lowpan_add_conn(struct l2cap_conn *conn);
+int bt_6lowpan_del_conn(struct l2cap_conn *conn);
+int bt_6lowpan_init(void);
+void bt_6lowpan_cleanup(void);
+
+#endif /* __6LOWPAN_H */
diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile
index 6a791e7..80cb215 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,6 +10,6 @@ obj-$(CONFIG_BT_HIDP)	+= hidp/
 
 bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
 	hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o sco.o lib.o \
-	a2mp.o amp.o
+	a2mp.o amp.o 6lowpan.o
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 0cef677..c47215f 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -40,6 +40,7 @@
 #include "smp.h"
 #include "a2mp.h"
 #include "amp.h"
+#include "6lowpan.h"
 
 bool disable_ertm;
 
@@ -6470,6 +6471,10 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb)
 			l2cap_conn_del(conn->hcon, EACCES);
 		break;
 
+	case L2CAP_FC_6LOWPAN:
+		bt_6lowpan_recv(conn, skb);
+		break;
+
 	default:
 		l2cap_data_channel(conn, cid, skb);
 		break;
@@ -6507,6 +6512,11 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
 	return exact ? lm1 : lm2;
 }
 
+static bool is_bt_6lowpan(void)
+{
+	return false;
+}
+
 void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 {
 	struct l2cap_conn *conn;
@@ -6515,8 +6525,12 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 
 	if (!status) {
 		conn = l2cap_conn_add(hcon);
-		if (conn)
+		if (conn) {
 			l2cap_conn_ready(conn);
+
+			if (hcon->type == LE_LINK && is_bt_6lowpan())
+				bt_6lowpan_add_conn(conn);
+		}
 	} else {
 		l2cap_conn_del(hcon, bt_to_errno(status));
 	}
@@ -6537,6 +6551,9 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
 	BT_DBG("hcon %p reason %d", hcon, reason);
 
+	if (hcon->type == LE_LINK && is_bt_6lowpan())
+		bt_6lowpan_del_conn(hcon->l2cap_data);
+
 	l2cap_conn_del(hcon, bt_to_errno(reason));
 }
 
@@ -6814,11 +6831,14 @@ int __init l2cap_init(void)
 	l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
 					    NULL, &l2cap_debugfs_fops);
 
+	bt_6lowpan_init();
+
 	return 0;
 }
 
 void l2cap_exit(void)
 {
+	bt_6lowpan_cleanup();
 	debugfs_remove(l2cap_debugfs);
 	l2cap_cleanup_sockets();
 }
-- 
1.7.11.7


^ permalink raw reply related

* [RFC v2 2/7] ipv6: Add checks for RAWIP ARP type
From: Jukka Rissanen @ 2013-10-30  9:00 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1383123661-15087-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 net/ipv6/addrconf.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index d6ff126..60bf947 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1783,6 +1783,15 @@ static int addrconf_ifid_ip6tnl(u8 *eui, struct net_device *dev)
 	return 0;
 }
 
+static int addrconf_ifid_rawip(u8 *eui, struct net_device *dev)
+{
+	if (dev->addr_len != 8)
+		return -1;
+	memcpy(eui, dev->dev_addr, 8);
+	eui[0] ^= 2;
+	return 0;
+}
+
 static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
 {
 	switch (dev->type) {
@@ -1803,6 +1812,8 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
 		return addrconf_ifid_ieee1394(eui, dev);
 	case ARPHRD_TUNNEL6:
 		return addrconf_ifid_ip6tnl(eui, dev);
+	case ARPHRD_RAWIP:
+		return addrconf_ifid_rawip(eui, dev);
 	}
 	return -1;
 }
@@ -2681,7 +2692,8 @@ static void addrconf_dev_config(struct net_device *dev)
 	    (dev->type != ARPHRD_INFINIBAND) &&
 	    (dev->type != ARPHRD_IEEE802154) &&
 	    (dev->type != ARPHRD_IEEE1394) &&
-	    (dev->type != ARPHRD_TUNNEL6)) {
+	    (dev->type != ARPHRD_TUNNEL6) &&
+	    (dev->type != ARPHRD_RAWIP)) {
 		/* Alas, we support only Ethernet autoconfiguration. */
 		return;
 	}
-- 
1.7.11.7


^ permalink raw reply related

* [RFC v2 1/7] net: if_arp: add ARPHRD_RAWIP type
From: Jukka Rissanen @ 2013-10-30  9:00 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1383123661-15087-1-git-send-email-jukka.rissanen@linux.intel.com>

This is used when there is no L2 header before IP header.
Example of this is Bluetooth 6LoWPAN network.

The RAWIP header type value is already used in some Android kernels
so same value is used here in order not to break userspace.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 include/uapi/linux/if_arp.h | 1 +
 1 file changed, 1 insertion(+)

diff --git a/include/uapi/linux/if_arp.h b/include/uapi/linux/if_arp.h
index d7fea34..06fc69f 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -59,6 +59,7 @@
 #define ARPHRD_LAPB	516		/* LAPB				*/
 #define ARPHRD_DDCMP    517		/* Digital's DDCMP protocol     */
 #define ARPHRD_RAWHDLC	518		/* Raw HDLC			*/
+#define ARPHRD_RAWIP	530	        /* Raw IP                       */
 
 #define ARPHRD_TUNNEL	768		/* IPIP tunnel			*/
 #define ARPHRD_TUNNEL6	769		/* IP6IP6 tunnel       		*/
-- 
1.7.11.7


^ permalink raw reply related

* [RFC v2 0/7] Bluetooth LE 6LoWPAN
From: Jukka Rissanen @ 2013-10-30  9:00 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

this is 6LoWPAN code for BT LE as described in
http://tools.ietf.org/html/draft-ietf-6lowpan-btle-12

v2:
- Change ARPHRD_IEEE802154 to ARPHRD_RAWIP. The generic code
  in patches 1 and 2 is also sent to netdev mailing list.
- Sending route exporting patch 5 to netdev ml
- Check private/public BT address and toggle universal/local bit
  accordingly in patch 3.
- The virtual interface template name is now shorter (bt%d)
- Various function name renames
- devtype of the interface set to "bluetooth"


v1:
- initial release


TODO:
- Discovery of 6LoWPAN service needs be automatic.
- Refactor compression and fragmentation code after these are fixed
  in net/ieee802154/6lowpan.c. Bluetooth 6LoWPAN should be able to share
  that part of the code.


Cheers,
Jukka


Jukka Rissanen (7):
  net: if_arp: add ARPHRD_RAWIP type
  ipv6: Add checks for RAWIP ARP type
  Bluetooth: Initial skeleton code for BT 6LoWPAN
  Bluetooth: Enable 6LoWPAN support for BT LE devices
  route: Exporting ip6_route_add() so that Bluetooth 6LoWPAN can use it
  Bluetooth: Set route to peer for 6LoWPAN
  Bluetooth: Manually enable or disable 6LoWPAN between devices

 include/net/bluetooth/l2cap.h |    1 +
 include/uapi/linux/if_arp.h   |    1 +
 net/bluetooth/6lowpan.c       | 1722 +++++++++++++++++++++++++++++++++++++++++
 net/bluetooth/6lowpan.h       |   28 +
 net/bluetooth/Makefile        |    2 +-
 net/bluetooth/hci_core.c      |    4 +
 net/bluetooth/l2cap_core.c    |   20 +-
 net/ipv6/addrconf.c           |   14 +-
 net/ipv6/route.c              |    1 +
 9 files changed, 1790 insertions(+), 3 deletions(-)
 create mode 100644 net/bluetooth/6lowpan.c
 create mode 100644 net/bluetooth/6lowpan.h

-- 
1.7.11.7


^ permalink raw reply

* Re: [PATCH 2/2] android: Suppress compiler warnings not used in autotools build
From: Andrei Emeltchenko @ 2013-10-30  8:37 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1664552.rzmdhg2vR5@uw000953>

Hi Szymon,

On Wed, Oct 30, 2013 at 09:25:03AM +0100, Szymon Janc wrote:
> Hi Andrei,
> 
> On Wednesday 30 of October 2013 10:19:15 Andrei Emeltchenko wrote:
> > Hi Szymon,
> > 
> > On Tue, Oct 29, 2013 at 04:23:38PM +0100, Szymon Janc wrote:
> > > Android build system is enabling some additional warnings that are not
> > > enabled when building with autotools. This avoids spurious warnings
> > > while building on Android.
> > > ---
> > >  android/Android.mk | 3 +++
> > >  1 file changed, 3 insertions(+)
> > > 
> > > diff --git a/android/Android.mk b/android/Android.mk
> > > index c88b27e..fc1b276 100644
> > > --- a/android/Android.mk
> > > +++ b/android/Android.mk
> > > @@ -9,6 +9,9 @@ pathmap_INCL += glib:external/bluetooth/glib
> > >  # Specify common compiler flags
> > >  BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\"
> > >  
> > > +# Disable warnings enabled by Android but not enabled in autotools build
> > > +BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith
> > 
> > I wouldn't put this to BLUEZ_COMMON_CFLAGS, maybe only bluetoothd target
> > CFLAGS, the issue withing HAL might be easily fixed. We use void pointer
> > in math operations.
> 
> This is done on purpose as using void* to operation is simply convenient as
> there is no need for char* casting (and sizeof(void *) == 1 is documented GCC
> feature). Further more I think it will be easier for developers if we can have
> same code guidelines and warnings while building on host and android for all
> components.

I just sent patch with some fix proposal. I know there are discussions
about this issue, let's maintainers decide on this.

PS: I personally think that casting when assigning 2 pointers is good in a
way it shows you that you cast to a different structure.

Best regards 
Andrei Emeltchenko 

^ permalink raw reply

* [PATCH] android/hal: Fix using void pointer in math operations
From: Andrei Emeltchenko @ 2013-10-30  8:27 UTC (permalink / raw)
  To: linux-bluetooth

From: Andrei Emeltchenko <andrei.emeltchenko@intel.com>

Fixes following compiler warnings:
...
hal-bluetooth.c: In function 'handle_adapter_props_changed':
hal-bluetooth.c:57:9: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
hal-bluetooth.c:57:29: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
hal-bluetooth.c:57:51: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
hal-bluetooth.c:66:5: warning: pointer of type 'void *' used in arithmetic [-Wpointer-arith]
...
---
 android/hal-bluetooth.c |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index fc60d72..0c083de 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -44,17 +44,18 @@ static void handle_adapter_props_changed(void *buf, uint16_t len)
 	struct hal_ev_adapter_props_changed *ev = buf;
 	bt_property_t props[ev->num_props];
 	struct hal_property *hal_prop;
-	void *p;
+	uint8_t *p;
 	int i;
 
 	if (!bt_hal_cbacks->adapter_properties_cb)
 		return;
 
 	hal_prop = ev->props;
-	p = ev->props;
+	p = (uint8_t *) ev->props;
 
 	for (i = 0; i < ev->num_props; i++) {
-		if (p + sizeof(*hal_prop) + hal_prop->len > buf + len) {
+		if (p + sizeof(*hal_prop) + hal_prop->len >
+							(uint8_t *) buf + len) {
 			error("invalid adapter properties event, aborting");
 			exit(EXIT_FAILURE);
 		}
@@ -64,7 +65,7 @@ static void handle_adapter_props_changed(void *buf, uint16_t len)
 		props[i].val = hal_prop->val;
 
 		p += sizeof(*hal_prop) + hal_prop->len;
-		hal_prop = p;
+		hal_prop = (struct hal_property *) p;
 
 		DBG("prop[%d]: %s", i, btproperty2str(&props[i]));
 	}
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH 2/2] android: Suppress compiler warnings not used in autotools build
From: Szymon Janc @ 2013-10-30  8:25 UTC (permalink / raw)
  To: Andrei Emeltchenko; +Cc: linux-bluetooth
In-Reply-To: <20131030081914.GH27517@aemeltch-MOBL1>

Hi Andrei,

On Wednesday 30 of October 2013 10:19:15 Andrei Emeltchenko wrote:
> Hi Szymon,
> 
> On Tue, Oct 29, 2013 at 04:23:38PM +0100, Szymon Janc wrote:
> > Android build system is enabling some additional warnings that are not
> > enabled when building with autotools. This avoids spurious warnings
> > while building on Android.
> > ---
> >  android/Android.mk | 3 +++
> >  1 file changed, 3 insertions(+)
> > 
> > diff --git a/android/Android.mk b/android/Android.mk
> > index c88b27e..fc1b276 100644
> > --- a/android/Android.mk
> > +++ b/android/Android.mk
> > @@ -9,6 +9,9 @@ pathmap_INCL += glib:external/bluetooth/glib
> >  # Specify common compiler flags
> >  BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\"
> >  
> > +# Disable warnings enabled by Android but not enabled in autotools build
> > +BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith
> 
> I wouldn't put this to BLUEZ_COMMON_CFLAGS, maybe only bluetoothd target
> CFLAGS, the issue withing HAL might be easily fixed. We use void pointer
> in math operations.

This is done on purpose as using void* to operation is simply convenient as
there is no need for char* casting (and sizeof(void *) == 1 is documented GCC
feature). Further more I think it will be easier for developers if we can have
same code guidelines and warnings while building on host and android for all
components.

-- 
BR
Szymon Janc


^ permalink raw reply

* Re: [PATCH 2/2] android: Suppress compiler warnings not used in autotools build
From: Andrei Emeltchenko @ 2013-10-30  8:19 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1383060218-25693-2-git-send-email-szymon.janc@tieto.com>

Hi Szymon,

On Tue, Oct 29, 2013 at 04:23:38PM +0100, Szymon Janc wrote:
> Android build system is enabling some additional warnings that are not
> enabled when building with autotools. This avoids spurious warnings
> while building on Android.
> ---
>  android/Android.mk | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/android/Android.mk b/android/Android.mk
> index c88b27e..fc1b276 100644
> --- a/android/Android.mk
> +++ b/android/Android.mk
> @@ -9,6 +9,9 @@ pathmap_INCL += glib:external/bluetooth/glib
>  # Specify common compiler flags
>  BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\"
>  
> +# Disable warnings enabled by Android but not enabled in autotools build
> +BLUEZ_COMMON_CFLAGS += -Wno-pointer-arith

I wouldn't put this to BLUEZ_COMMON_CFLAGS, maybe only bluetoothd target
CFLAGS, the issue withing HAL might be easily fixed. We use void pointer
in math operations.

Best regards 
Andrei Emeltchenko 

^ permalink raw reply

* Re: [PATCH BlueZ] audio/AVRCP: Always assign a player to TG role
From: Johan Hedberg @ 2013-10-30  7:39 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth
In-Reply-To: <1383055634-11953-1-git-send-email-luiz.dentz@gmail.com>

Hi Luiz,

On Tue, Oct 29, 2013, Luiz Augusto von Dentz wrote:
> Currently the code does not assign a player if version is bellow 1.3,
> but sometimes the version may not be initialized correctly e.g: record
> could not be fetch.
> 
> Futhermore by assigning a player for 1.0 CT it won't break backward
> compatibility as no commands will be sent but adds the possibility to
> respond properly in case the CT is sending commands above its own
> version which is a violation of the spec but unfortunately there exists
> such stacks on the market.
> ---
>  profiles/audio/avrcp.c | 3 ---
>  1 file changed, 3 deletions(-)

Applied. Thanks.

Johan

^ permalink raw reply

* Re: [PATCH] Bluetooth: Set default own address type only during controller setup
From: Johan Hedberg @ 2013-10-30  7:34 UTC (permalink / raw)
  To: Marcel Holtmann; +Cc: linux-bluetooth
In-Reply-To: <1383074811-6623-1-git-send-email-marcel@holtmann.org>

Hi Marcel,

On Tue, Oct 29, 2013, Marcel Holtmann wrote:
> The default own address type is currently set at every power on of
> a controller. This overwrites the value set via debugfs. To avoid
> this issue, set the default own address type only during controller
> setup.
> 
> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
> ---
>  net/bluetooth/hci_core.c | 20 +++++++++++---------
>  1 file changed, 11 insertions(+), 9 deletions(-)

Applied to bluetooth-next. Thanks.

Johan

^ permalink raw reply

* Re: [PATCH] btusb: add realtek 8723 in the blacklist
From: Marcel Holtmann @ 2013-10-30  7:21 UTC (permalink / raw)
  To: Bruce.Ma; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <527090CF.9080606@canonical.com>

Hi Bruce,

> From: Bruce Ma <bruce.ma@canonical.com>
> Date: Tue, 22 Oct 2013 11:21:20 +0800
> Subject: [PATCH] btusb: add realtek 8723 in the blacklist
> 
> The vendor realtek provides an off-tree driver for realtek 8723, so we
> add realtek 8723 in the in-tree driver's blacklist.

we do not care about out-of-tree drivers. Get the Realtek driver merged upstream.

> Signed-off-by: Bruce Ma <bruce.ma@canonical.com>
> ---
> drivers/bluetooth/btusb.c | 3 +++
> 1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
> index eae58a1..3097ac8 100644
> --- a/drivers/bluetooth/btusb.c
> +++ b/drivers/bluetooth/btusb.c
> @@ -138,6 +138,9 @@ static struct usb_device_id blacklist_table[] = {
>     { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
>     { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
> 
> +    /* Realtek 8723 Bluetooth */
> +    { USB_DEVICE(0x0bda, 0xb728), .driver_info = BTUSB_IGNORE },
> +
>     /* Atheros AR9285 Malbec with sflash firmware */
>     { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },

And as an extra advice, using proper tabs vs spaces is important.

Regards

Marcel


^ permalink raw reply

* [PATCH] btusb: add realtek 8723 in the blacklist
From: Bruce.Ma @ 2013-10-30  4:53 UTC (permalink / raw)
  To: linux-bluetooth

From: Bruce Ma <bruce.ma@canonical.com>
Date: Tue, 22 Oct 2013 11:21:20 +0800
Subject: [PATCH] btusb: add realtek 8723 in the blacklist

The vendor realtek provides an off-tree driver for realtek 8723, so we
add realtek 8723 in the in-tree driver's blacklist.

Signed-off-by: Bruce Ma <bruce.ma@canonical.com>
---
 drivers/bluetooth/btusb.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index eae58a1..3097ac8 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -138,6 +138,9 @@ static struct usb_device_id blacklist_table[] = {
     { USB_DEVICE(0x0489, 0xe03d), .driver_info = BTUSB_IGNORE },
     { USB_DEVICE(0x0489, 0xe027), .driver_info = BTUSB_IGNORE },
 
+    /* Realtek 8723 Bluetooth */
+    { USB_DEVICE(0x0bda, 0xb728), .driver_info = BTUSB_IGNORE },
+
     /* Atheros AR9285 Malbec with sflash firmware */
     { USB_DEVICE(0x03f0, 0x311d), .driver_info = BTUSB_IGNORE },
 
-- 
1.8.1.2


^ permalink raw reply related

* Re: Intel 7260 bluetooth malfunction when it is connected to EHCI bus
From: Hui Wang @ 2013-10-30  2:51 UTC (permalink / raw)
  To: Marcel Holtmann
  Cc: Tedd An, Johan Hedberg, xiong.y.zhang, Gustavo F. Padovan,
	linux-bluetooth@vger.kernel.org development
In-Reply-To: <A07820C9-C272-4AD4-AE16-49F81956C2D7@holtmann.org>

On 10/29/2013 07:16 PM, Marcel Holtmann wrote:
> Hi Hui,
>
>> The problem is:
>> On the machine which has Intel 7260 BT module, i use it to connect a bluetooth headset,
>> it can successfully scan and connect to the headset, when i play sound to the bt headset,
>> the problem comes, if the bt module is connected to the XHCI, it can work very well.
>>
>> u@u-Lenovo-B4400:~$ lsusb -t
>> 1-7:1.0: No such file or directory
>> /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
>> /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/14p, 480M
>>     |__ Port 6: Dev 8, If 0, Class=HID, Driver=usbhid, 1.5M
>>     |__ Port 7: Dev 2, If 0, Class=vend., Driver=, 12M
>>     |__ Port 11: Dev 3, If 0, Class='bInterfaceClass 0xe0 not yet handled', Driver=btusb, 12M
>>     |__ Port 11: Dev 3, If 1, Class='bInterfaceClass 0xe0 not yet handled', Driver=btusb, 12M
>>
>> But if the bt module is connected to the EHCI, it always fails to play sound.
>>
>> u@u-Lenovo-B4400:~$ lsusb -t
>> /: Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
>> /: Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/14p, 480M
>> /: Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/3p, 480M
>>     |__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/8p, 480M
>> /: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci_hcd/3p, 480M
>>     |__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/6p, 480M
>>         |__ Port 5: Dev 3, If 0, Class='bInterfaceClass 0xe0 not yet handled', Driver=btusb, 12M
>>         |__ Port 5: Dev 3, If 1, Class='bInterfaceClass 0xe0 not yet handled', Driver=btusb, 12M
>>         |__ Port 6: Dev 4, If 0, Class='bInterfaceClass 0x0e not yet handled', Driver=uvcvideo, 480M
>>         |__ Port 6: Dev 4, If 1, Class='bInterfaceClass 0x0e not yet handled', Driver=uvcvideo, 480M
> can you paste /sys/kernel/debug/usb/devices here.
>
> If the uvcvideo driver is also using ISOC URBs, we might have just exhausted the bandwidth of the USB bus here or these two devices just do not play nice with each other.
>
> Try to unload the uvcvideo driver and try it again.
Thanks for your reply, follow your instruction, i totally disabled the 
camera from the BIOS, then the usb tree like this,
  u@u-Lenovo-B4400:~$ lsusb -t
1-1.3:1.0: No such file or directory
/:  Bus 04.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/4p, 5000M
/:  Bus 03.Port 1: Dev 1, Class=root_hub, Driver=xhci_hcd/14p, 480M
/:  Bus 02.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/3p, 480M
     |__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/8p, 480M
/:  Bus 01.Port 1: Dev 1, Class=root_hub, Driver=ehci-pci/3p, 480M
     |__ Port 1: Dev 2, If 0, Class=hub, Driver=hub/6p, 480M
         |__ Port 2: Dev 3, If 0, Class=HID, Driver=usbhid, 1.5M
         |__ Port 3: Dev 4, If 0, Class=vend., Driver=, 12M
         |__ Port 5: Dev 5, If 0, Class='bInterfaceClass 0xe0 not yet 
handled', Driver=btusb, 12M
         |__ Port 5: Dev 5, If 1, Class='bInterfaceClass 0xe0 not yet 
handled', Driver=btusb, 12M
u@u-Lenovo-B4400:~$ uname -a
Linux u-Lenovo-B4400 3.12.0-031200rc6-generic #201310191635 SMP Sat Oct 
19 20:36:43 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

I re-did the test, the result is same as before, after the headset is 
connected and i play audio, no sound is heard from the headset.
And hciconfig output shows still no SCO packets received by BT driver.
u@u-Lenovo-B4400:~$ hciconfig
hci0:    Type: BR/EDR  Bus: USB
     BD Address: 00:15:00:CC:2D:D2  ACL MTU: 1021:5  SCO MTU: 96:5
     UP RUNNING PSCAN
     RX bytes:5853 acl:61 sco:0 events:298 errors:0
     TX bytes:19212 acl:68 sco:1 commands:179 errors:0

This is the debug output of "sudo cat /sys/kernel/debug/usb/devices" 
after those operations,
u@u-Lenovo-B4400:~$ sudo cat /sys/kernel/debug/usb/devices

T:  Bus=04 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=5000 MxCh= 4
B:  Alloc=  0/800 us ( 0%), #Int=  0, #Iso=  0
D:  Ver= 3.00 Cls=09(hub  ) Sub=00 Prot=03 MxPS= 9 #Cfgs=  1
P:  Vendor=1d6b ProdID=0003 Rev= 3.05
S:  Manufacturer=Linux 3.12.0-rc6-WR5.0.1.4_standard+ xhci_hcd
S:  Product=xHCI Host Controller
S:  SerialNumber=0000:00:14.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=256ms

T:  Bus=03 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=480  MxCh=14
B:  Alloc=  0/800 us ( 0%), #Int=  0, #Iso=  0
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=1d6b ProdID=0002 Rev= 3.05
S:  Manufacturer=Linux 3.12.0-rc6-WR5.0.1.4_standard+ xhci_hcd
S:  Product=xHCI Host Controller
S:  SerialNumber=0000:00:14.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=256ms

T:  Bus=02 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=480  MxCh= 3
B:  Alloc=  0/800 us ( 0%), #Int=  0, #Iso=  0
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=1d6b ProdID=0002 Rev= 3.05
S:  Manufacturer=Linux 3.12.0-rc6-WR5.0.1.4_standard+ ehci_hcd
S:  Product=EHCI Host Controller
S:  SerialNumber=0000:00:1d.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=256ms

T:  Bus=02 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480  MxCh= 8
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=8087 ProdID=8000 Rev= 0.05
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   2 Ivl=256ms

T:  Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#=  1 Spd=480  MxCh= 3
B:  Alloc=  4/800 us ( 1%), #Int=  3, #Iso=  1
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
P:  Vendor=1d6b ProdID=0002 Rev= 3.05
S:  Manufacturer=Linux 3.12.0-rc6-WR5.0.1.4_standard+ ehci_hcd
S:  Product=EHCI Host Controller
S:  SerialNumber=0000:00:1a.0
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=256ms

T:  Bus=01 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#=  2 Spd=480  MxCh= 6
D:  Ver= 2.00 Cls=09(hub  ) Sub=00 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=8087 ProdID=8008 Rev= 0.05
C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=  0mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=09(hub  ) Sub=00 Prot=00 Driver=hub
E:  Ad=81(I) Atr=03(Int.) MxPS=   1 Ivl=256ms

T:  Bus=01 Lev=02 Prnt=02 Port=01 Cnt=01 Dev#=  3 Spd=1.5  MxCh= 0
D:  Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs=  1
P:  Vendor=0461 ProdID=4d22 Rev= 2.00
S:  Product=USB Optical Mouse
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 1 Cls=03(HID  ) Sub=01 Prot=02 Driver=usbhid
E:  Ad=81(I) Atr=03(Int.) MxPS=   4 Ivl=10ms

T:  Bus=01 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#=  4 Spd=12   MxCh= 0
D:  Ver= 1.10 Cls=ff(vend.) Sub=11 Prot=ff MxPS= 8 #Cfgs=  1
P:  Vendor=138a ProdID=0011 Rev= 0.78
S:  SerialNumber=7ffcdfda8afe
C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 4 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none)
E:  Ad=01(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=81(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=83(I) Atr=03(Int.) MxPS=   8 Ivl=4ms

T:  Bus=01 Lev=02 Prnt=02 Port=04 Cnt=03 Dev#=  5 Spd=12   MxCh= 0
D:  Ver= 2.00 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs=  1
P:  Vendor=8087 ProdID=07dc Rev= 0.01
C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA
I:* If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=81(I) Atr=03(Int.) MxPS=  64 Ivl=1ms
E:  Ad=02(O) Atr=02(Bulk) MxPS=  64 Ivl=0ms
E:  Ad=82(I) Atr=02(Bulk) MxPS=  64 Ivl=0ms
I:  If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=   0 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=   0 Ivl=1ms
I:  If#= 1 Alt= 1 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=   9 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=   9 Ivl=1ms
I:* If#= 1 Alt= 2 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  17 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  17 Ivl=1ms
I:  If#= 1 Alt= 3 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  25 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  25 Ivl=1ms
I:  If#= 1 Alt= 4 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  33 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  33 Ivl=1ms
I:  If#= 1 Alt= 5 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb
E:  Ad=03(O) Atr=01(Isoc) MxPS=  49 Ivl=1ms
E:  Ad=83(I) Atr=01(Isoc) MxPS=  49 Ivl=1ms


Thanks,
Hui.

>
> Regards
>
> Marcel
>
>
>

^ permalink raw reply

* Re: [RFC v2 05/15] Bluetooth: Make find_conn_param() helper non-local
From: Marcel Holtmann @ 2013-10-29 23:33 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-6-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch makes the find_conn_param() helper non-local by adding the
> hci_ prefix and declaring it in hci_core.h. This helper will be used
> in hci_conn.c to get the connection parameters when establishing
> connections.
> 
> Since hci_find_conn_param() returns a reference to the hci_conn_param
> object, it was added a refcount to hci_conn_param to control its
> lifetime. This way, we avoid bugs such as use-after-free.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> include/net/bluetooth/hci_core.h |  5 +++++
> net/bluetooth/hci_core.c         | 45 ++++++++++++++++++++++++++++++++++------
> 2 files changed, 44 insertions(+), 6 deletions(-)
> 
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 22d16d9..64911aa 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -378,6 +378,8 @@ struct hci_chan {
> };
> 
> struct hci_conn_params {
> +	struct kref refcount;
> +
> 	struct list_head list;
> 
> 	bdaddr_t addr;
> @@ -767,6 +769,9 @@ int hci_add_conn_params(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
> 			u16 conn_interval_max);
> void hci_remove_conn_params(struct hci_dev *hdev, bdaddr_t *addr,
> 			    u8 addr_type);
> +struct hci_conn_params *hci_find_conn_params(struct hci_dev *hdev,
> +					   bdaddr_t *addr, u8 addr_type);
> +void hci_conn_params_put(struct hci_conn_params *params);
> 
> int hci_uuids_clear(struct hci_dev *hdev);
> 
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index fa41a58..0a278da 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2771,8 +2771,33 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
> 	return mgmt_device_unblocked(hdev, bdaddr, type);
> }
> 
> -static struct hci_conn_params *find_conn_params(struct hci_dev *hdev,
> -						bdaddr_t *addr, u8 addr_type)
> +static void hci_conn_params_get(struct hci_conn_params *params)
> +{
> +	kref_get(&params->refcount);
> +}

Lets have return hci_conn_params_get the structure itself and give it the parameters we are looking for. So it is essentially the lookup function. That is how it is used anyway.

> +
> +static void release_hci_conn_params(struct kref *kref)
> +{
> +	struct hci_conn_params *params = container_of(kref,
> +						    struct hci_conn_params,
> +						    refcount);
> +
> +	kfree(params);
> +}
> +
> +void hci_conn_params_put(struct hci_conn_params *params)
> +{
> +	kref_put(&params->refcount, release_hci_conn_params);
> +}
> +
> +/* Lookup hci_conn_params in hdev->conn_params list.
> + *
> + * Return a reference to hci_conn_params object with refcount incremented.
> + * The caller should drop its reference by using hci_conn_params_put(). If
> + * hci_conn_params is not found, NULL is returned.
> + */
> +struct hci_conn_params *hci_find_conn_params(struct hci_dev *hdev,
> +					     bdaddr_t *addr, u8 addr_type)
> {
> 	struct hci_conn_params *params;
> 
> @@ -2784,6 +2809,8 @@ static struct hci_conn_params *find_conn_params(struct hci_dev *hdev,
> 		if (params->addr_type != addr_type)
> 			continue;
> 
> +		hci_conn_params_get(params);
> +
> 		rcu_read_unlock();
> 		return params;
> 	}
> @@ -2798,14 +2825,18 @@ int hci_add_conn_params(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
> {
> 	struct hci_conn_params *params;
> 
> -	params = find_conn_params(hdev, addr, addr_type);
> -	if (params)
> +	params = hci_find_conn_params(hdev, addr, addr_type);
> +	if (params) {
> +		hci_conn_params_put(params);
> 		return -EEXIST;
> +	}
> 
> 	params = kmalloc(sizeof(*params), GFP_KERNEL);
> 	if (!params)
> 		return -ENOMEM;
> 
> +	kref_init(&params->refcount);
> +
> 	bacpy(&params->addr, addr);
> 	params->addr_type = addr_type;
> 	params->auto_connect = auto_connect;
> @@ -2827,20 +2858,22 @@ static void __remove_conn_params(struct hci_conn_params *params)
> 	list_del_rcu(&params->list);
> 	synchronize_rcu();
> 
> -	kfree(params);
> +	hci_conn_params_put(params);
> }
> 
> void hci_remove_conn_params(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type)
> {
> 	struct hci_conn_params *params;
> 
> -	params = find_conn_params(hdev, addr, addr_type);
> +	params = hci_find_conn_params(hdev, addr, addr_type);
> 	if (!params)
> 		return;
> 
> 	hci_dev_lock(hdev);
> 	__remove_conn_params(params);
> 	hci_dev_unlock(hdev);
> +
> +	hci_conn_params_put(params);
> }

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 09/15] Bluetooth: Introduce LE auto connection infrastructure
From: Marcel Holtmann @ 2013-10-29 23:30 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-10-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch introduces the LE auto connection infrastructure.
> This infrastructure will be used to implement the auto_connect
> options from hci_conn_params.
> 
> The auto connection mechanism works as follows: When the first
> device address is inserted into hdev->pending_auto_con list, we
> start the background scanning. Once the target device is found
> in range, the kernel creates the connection. If connection is
> established successfully, the device address is removed from
> hdev->pending_auto_conn list. Finally, when the last device
> address is removed, the background scanning is stopped.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> include/net/bluetooth/hci_core.h |   2 +
> net/bluetooth/hci_conn.c         |   6 ++
> net/bluetooth/hci_core.c         | 115 +++++++++++++++++++++++++++++++++++++++
> net/bluetooth/hci_event.c        |  43 +++++++++++++++
> 4 files changed, 166 insertions(+)
> 
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 0049036..322918f 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -784,6 +784,8 @@ void __hci_remove_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> 
> bool hci_is_scan_and_conn_supported(struct hci_dev *hdev);
> 
> +void hci_check_background_scan(struct hci_dev *hdev);
> +
> int hci_uuids_clear(struct hci_dev *hdev);
> 
> int hci_link_keys_clear(struct hci_dev *hdev);
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index b52bcb2..bbe3cab 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -544,6 +544,12 @@ static void create_le_conn_complete(struct hci_dev *hdev, u8 status)
> 
> done:
> 	hci_dev_unlock(hdev);
> +
> +	/* Check the background scanning since it may have been temporarily
> +	 * stopped if the controller doesn't support scanning and creating
> +	 * connection at the same time.
> +	 */
> +	hci_check_background_scan(hdev);
> }
> 
> static int hci_create_le_conn(struct hci_conn *conn)
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 19624d1..79debc3 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2923,6 +2923,72 @@ bool hci_has_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> 	return res;
> }
> 
> +static void start_background_scan_complete(struct hci_dev *hdev, u8 status)
> +{
> +	if (status)
> +		BT_DBG("HCI request failed to start background scanning: "
> +		       "status 0x%2.2x", status);
> +}
> +
> +static int start_background_scan(struct hci_dev *hdev)
> +{
> +	struct hci_cp_le_set_scan_param param_cp;
> +	struct hci_cp_le_set_scan_enable enable_cp;
> +	struct hci_request req;
> +
> +	if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
> +		return 0;
> +
> +	BT_DBG("%s", hdev->name);
> +
> +	hci_req_init(&req, hdev);
> +
> +	memset(&param_cp, 0, sizeof(param_cp));
> +	param_cp.type = LE_SCAN_PASSIVE;
> +	param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
> +	param_cp.window = cpu_to_le16(hdev->le_scan_window);
> +	hci_req_add(&req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
> +		    &param_cp);
> +
> +	memset(&enable_cp, 0, sizeof(enable_cp));
> +	enable_cp.enable = LE_SCAN_ENABLE;
> +	enable_cp.filter_dup = LE_SCAN_FILTER_DUP_DISABLE;
> +	hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
> +		    &enable_cp);
> +
> +	return hci_req_run(&req, start_background_scan_complete);
> +}
> +
> +static void stop_background_scan_complete(struct hci_dev *hdev, u8 status)
> +{
> +	if (status)
> +		BT_DBG("HCI request failed to stop background scanning: "
> +		       "status 0x%2.2x", status);
> +}
> +
> +static int stop_background_scan(struct hci_dev *hdev)
> +{
> +	struct hci_cp_le_set_scan_enable cp;
> +	struct hci_request req;
> +
> +	if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
> +		return 0;

The background scanning needs its own HCI_LE_BG_SCAN flag.

> +
> +	/* If device discovery is running, background scanning is stopped so
> +	 * we return sucess.
> +	 */
> +	if (hdev->discovery.state == DISCOVERY_FINDING)
> +		return 0;
> +
> +	hci_req_init(&req, hdev);
> +
> +	memset(&cp, 0, sizeof(cp));
> +	cp.enable = LE_SCAN_DISABLE;
> +	hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
> +
> +	return hci_req_run(&req, stop_background_scan_complete);
> +}
> +
> /* This function requires the caller holds hdev->lock */
> int __hci_add_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> 				u8 addr_type)
> @@ -2940,6 +3006,19 @@ int __hci_add_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> 	bacpy(&entry->bdaddr, addr);
> 	entry->bdaddr_type = addr_type;
> 
> +	/* If there is no pending auto connection, the background scanning
> +	 * is not runnning. So we should start it.
> +	 */
> +	if (list_empty(&hdev->pending_auto_conn)) {
> +		int err;
> +
> +		err = start_background_scan(hdev);
> +		if (err) {
> +			kfree(entry);
> +			return err;
> +		}
> +	}
> +
> 	list_add(&entry->list, &hdev->pending_auto_conn);
> 
> 	return 0;
> @@ -2957,6 +3036,14 @@ void __hci_remove_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> 
> 	list_del(&entry->list);
> 	kfree(entry);
> +
> +	if (list_empty(&hdev->pending_auto_conn)) {
> +		int err;
> +
> +		err = stop_background_scan(hdev);
> +		if (err)
> +			BT_ERR("Failed to run HCI request: err %d", err);

I am getting sick of these errors. Return an error from a function if you plan to do something with it. Otherwise just don’t. Not to mention that the only one possible here is ENOMEM. That I declared tons and tons of functions void in bluetooth-next might have been an indication that we are not doing this anymore.

> +	}
> }

And this should be all one function. Move the list_empty check into the function that updates the background scanning status. This all feels cluttered.

I mean at the end of the day it is pretty simple logic. If the list is empty stop scanning and if the list has at least one item, start scanning. Then deal with the cases where we have discovery, connection attempts etc. And done. I do not want to check 20 different locations that we call the right function with the right if around it.

> 
> /* This function requires the caller holds hdev->lock */
> @@ -2970,6 +3057,34 @@ static void __clear_pending_auto_conn(struct hci_dev *hdev)
> 	}
> }
> 
> +/* This function starts the background scanning in case there is still
> + * pending auto connections.
> + */
> +void hci_check_background_scan(struct hci_dev *hdev)
> +{
> +	struct hci_conn *conn;
> +	int err;
> +
> +	if (list_empty(&hdev->pending_auto_conn))
> +		return;
> +
> +	if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
> +		return;
> +
> +	/* If the controller is connecting but it doesn't support scanning
> +	 * scanning and connecting at the same time we don't start the
> +	 * background scanning now. It will be started once the connection
> +	 * is established.
> +	 */
> +	conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
> +	if (conn && !hci_is_scan_and_conn_supported(hdev))
> +		return;
> +
> +	err = start_background_scan(hdev);
> +	if (err)
> +		BT_ERR("Failed to start background scanning: err %d", err);

The err crap has to go away. It is pretty much annoying me now.

> +}
> +
> static void inquiry_complete(struct hci_dev *hdev, u8 status)
> {
> 	if (status) {
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 8b7cd37..e48601d 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -3547,8 +3547,49 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
> 
> 	hci_proto_connect_cfm(conn, ev->status);
> 
> +	__hci_remove_pending_auto_conn(hdev, &ev->bdaddr, ev->bdaddr_type);
> +
> unlock:
> 	hci_dev_unlock(hdev);
> +
> +	/* Check the background scanning since it may have been temporarily
> +	 * stopped if the controller doesn't support scanning and creating
> +	 * connection at the same time.
> +	 */
> +	hci_check_background_scan(hdev);

I have no idea why hci_remove_pending_auto_conn can not also call the code for updating the scanning status. Make it really simple.

> +}
> +
> +static void check_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +				    u8 addr_type)
> +{
> +	struct hci_conn *conn;
> +	u8 bdaddr_type;
> +
> +	if (!hci_has_pending_auto_conn(hdev, addr, addr_type))
> +		return;
> +
> +	if (addr_type == ADDR_LE_DEV_PUBLIC)
> +		bdaddr_type = BDADDR_LE_PUBLIC;
> +	else
> +		bdaddr_type = BDADDR_LE_RANDOM;
> +
> +	conn = hci_connect(hdev, LE_LINK, addr, bdaddr_type, BT_SECURITY_LOW,
> +			   HCI_AT_NO_BONDING);
> +	if (IS_ERR(conn)) {

Just returning is !IS_ERR(conn) is better to read.

> +		switch (PTR_ERR(conn)) {
> +		case -EBUSY:
> +			/* When hci_connect() returns EBUSY it means there is
> +			 * already an LE connection attempt going on. Since the
> +			 * controller supports only one connection attempt at
> +			 * the time, we simply return.
> +			 */
> +			return;

And this can be a break then.

> +		default:
> +			BT_ERR("Failed to auto connect: err %ld",
> +			       PTR_ERR(conn));
> +			return;

Same here.

> +		}
> +	}
> }

Also do we really care with printing an error here.

> 
> static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
> @@ -3560,6 +3601,8 @@ static void hci_le_adv_report_evt(struct hci_dev *hdev, struct sk_buff *skb)
> 	while (num_reports--) {
> 		struct hci_ev_le_advertising_info *ev = ptr;
> 
> +		check_pending_auto_conn(hdev, &ev->bdaddr, ev->bdaddr_type);
> +
> 		rssi = ev->data[ev->length];
> 		mgmt_device_found(hdev, &ev->bdaddr, LE_LINK, ev->bdaddr_type,
> 				  NULL, rssi, 0, 1, ev->data, ev->length);

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 10/15] Bluetooth: Temporarily stop background scanning on discovery
From: Marcel Holtmann @ 2013-10-29 23:19 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-11-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> If the user send a mgmt start discovery command while the background
> scanning is running, we should temporarily stop it. Once the discovery
> finishes, we start the background scanning again.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> net/bluetooth/hci_core.c |  5 +++++
> net/bluetooth/mgmt.c     | 12 ++++++++----
> 2 files changed, 13 insertions(+), 4 deletions(-)
> 
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 79debc3..44d3f99 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -1496,6 +1496,11 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state)
> 
> 	switch (state) {
> 	case DISCOVERY_STOPPED:
> +		/* Check the background scanning since it may have been
> +		 * temporarily stopped by the start discovery command.
> +		 */
> +		hci_check_background_scan(hdev);
> +

I do not know what check here means. For me this is reenable_background_scan or trigger_background_scan or start_background_scan, but not some magic check.

> 		if (hdev->discovery.state != DISCOVERY_STARTING)
> 			mgmt_discovering(hdev, 0);
> 		break;
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 22cf547..0e329e5 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -3280,11 +3280,15 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev,
> 			goto failed;
> 		}
> 
> +		/* If controller is scanning, it means the background scanning
> +		 * is running. Thus, we should temporarily stop it in order to
> +		 * set the discovery scanning parameters.
> +		 */
> 		if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) {
> -			err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY,
> -					 MGMT_STATUS_BUSY);
> -			mgmt_pending_remove(cmd);
> -			goto failed;
> +			memset(&enable_cp, 0, sizeof(enable_cp));
> +			enable_cp.enable = LE_SCAN_DISABLE;
> +			hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE,
> +				    sizeof(enable_cp), &enable_cp);
> 		}

I think we need a new hdev->dev_flags for HCI_LE_BG_SCAN. Magically assuming that it is a background scan is dangerous.

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 13/15] Bluetooth: Add thread-safe version of helpers
From: Marcel Holtmann @ 2013-10-29 23:16 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-14-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch adds the thread-safe version of helper functions to add
> and remove pending auto connections. These helpers will be used in
> next patches to implementing the Mgmt add/remove connection
> parameters commands.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> include/net/bluetooth/hci_core.h |  4 ++++
> net/bluetooth/hci_core.c         | 20 ++++++++++++++++++++
> 2 files changed, 24 insertions(+)

and here is where I would stop and start with a debugfs interface. So we can start testing this. Going straight for mgmt without seeing how it all works would be a little bit too early for me.

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 12/15] Bleutooth: Add support for auto connect options
From: Marcel Holtmann @ 2013-10-29 23:15 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-13-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch adds support for the HCI_AUTO_CONN_ALWAYS and HCI_AUTO_
> CONN_LINK_LOSS options from struct hci_conn_params.
> 
> The HCI_AUTO_CONN_ALWAYS option configures the kernel to always re-
> establish the connection, no matter the reason the connection was
> terminated. This feature is required by some LE profiles such as
> HID over GATT, Health Thermometer and Blood Pressure. These profiles
> require the host autonomously connect to the device as soon as it
> enters in connectable mode (start advertising) so the device is able
> to delivery a notification or indication.
> 
> The BT_AUTO_CONN_LINK_LOSS option configures the kernel to re-
> establish the connection in case the connection was terminated due
> to a link loss. This feature is required by the majority of LE
> profiles such as Proximity, Find Me, Cycling Speed and Cadence and
> Time.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> net/bluetooth/hci_event.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
> 
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index e48601d..b2d8aee 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -1785,6 +1785,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
> 	struct hci_conn *conn;
> 	u8 type;
> 	bool send_mgmt_event = false;
> +	struct hci_conn_params *params;
> 
> 	BT_DBG("%s status 0x%2.2x", hdev->name, ev->status);
> 
> @@ -1817,6 +1818,31 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
> 	if (conn->type == ACL_LINK && conn->flush_key)
> 		hci_remove_link_key(hdev, &conn->dst);
> 
> +	params = hci_find_conn_params(hdev, &conn->dst, conn->dst_type);
> +	if (params) {
> +		int err;
> +
> +		switch (params->auto_connect) {
> +		case HCI_AUTO_CONN_LINK_LOSS:
> +			if (ev->reason != HCI_ERROR_CONNECTION_TIMEOUT)
> +				break;
> +			/* Fall through */
> +
> +		case HCI_AUTO_CONN_ALWAYS:
> +			err = __hci_add_pending_auto_conn(hdev, &conn->dst,
> +							  conn->dst_type);
> +			if (err)
> +				BT_ERR("Failed to add pending auto connection "
> +				       " %d", err);

I really wonder what we are getting from pushing the errors out here if the only thing we do is print an error message anyway. I do not see the point. Print the error where it happens and make the rest void.

> +			break;
> +
> +		default:
> +			break;
> +		}
> +
> +		hci_conn_params_put(params);
> +	}
> +
> 	type = conn->type;
> 	hci_proto_disconn_cfm(conn, ev->reason);
> 	hci_conn_del(conn);

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 11/15] Bluetooth: Auto connection and power on
From: Marcel Holtmann @ 2013-10-29 23:13 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-12-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch implements a fixup required by auto connection mechanism
> in order to work properly after a power on.
> 
> When hdev is closed (e.g. Mgmt power off command, RFKILL or controller
> is reset), the ongoing active connections are silently dropped by the
> controller (no Disconnection Complete Event is sent to host). For that
> reason, the devices that require HCI_AUTO_CONN_ALWAYS are not added to
> hdev->pending_auto_conn list and they won't auto connect. So to fix
> this issue, after adapter is powered on, we should add all HCI_AUTO_
> CONN_ALWAYS address to hdev->pending_auto_conn list. Besides that, we
> always have to check if there are pending auto connections and start
> the background scanning if it is the case.
> 
> This way, the auto connection mechanism works propely after a power
> off and power on sequence as well as RFKILL block/unblock.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> include/net/bluetooth/hci_core.h |  2 ++
> net/bluetooth/hci_core.c         | 49 ++++++++++++++++++++++++++++++++++++++++
> net/bluetooth/mgmt.c             |  2 ++
> 3 files changed, 53 insertions(+)
> 
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 322918f..379bb36 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -786,6 +786,8 @@ bool hci_is_scan_and_conn_supported(struct hci_dev *hdev);
> 
> void hci_check_background_scan(struct hci_dev *hdev);
> 
> +void __hci_fixup_auto_conn(struct hci_dev *hdev);
> +
> int hci_uuids_clear(struct hci_dev *hdev);
> 
> int hci_link_keys_clear(struct hci_dev *hdev);
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 44d3f99..63a56f5 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -3090,6 +3090,55 @@ void hci_check_background_scan(struct hci_dev *hdev)
> 		BT_ERR("Failed to start background scanning: err %d", err);
> }
> 
> +/* This functions implements a fixup required by auto connection mechanism
> + * in order to work properly after a power on.
> + *
> + * When hdev is closed (e.g. Mgmt power off command, RFKILL or controller is
> + * reset), the ongoing active connections are silently dropped by the
> + * by the controller (no Disconnection Complete Event is sent to host). For
> + * that reason, the devices that require HCI_AUTO_CONN_ALWAYS are not add to
> + * hdev->pending_auto_conn list and they won't auto connect. So to fix this
> + * issue, after adapter is powered on, we should add all HCI_AUTO_CONN_ALWAYS
> + * address to hdev->pending_auto_conn list. Besides that, we always have to
> + * check if there are pending auto connections and start the background
> + * scanning if it is the case.
> + *
> + * This function requires the caller holds hdev->lock.
> + */
> +void __hci_fixup_auto_conn(struct hci_dev *hdev)

I have no idea why you call this a fixup function. For me this is normal procedure. On power down we should clear the list of pending connections and on power up we re-establish it.

> +{
> +	struct hci_conn_params *p;
> +	struct bdaddr_list *entry;
> +
> +	list_for_each_entry(p, &hdev->conn_params, list) {
> +		if (p->auto_connect != HCI_AUTO_CONN_ALWAYS)
> +			continue;
> +
> +		entry = __find_pending_auto_conn(hdev, &p->addr, p->addr_type);
> +		if (entry)
> +			continue;

Why are we bothering here with checking if it exists. The list should be empty when we start.

> +
> +		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +		if (!entry) {
> +			BT_ERR("Out of memory of auto connection");
> +			return;
> +		}
> +
> +		bacpy(&entry->bdaddr, &p->addr);
> +		entry->bdaddr_type = p->addr_type;
> +		list_add(&entry->list, &hdev->pending_auto_conn);
> +	}
> +
> +	if (!list_empty(&hdev->pending_auto_conn)) {
> +		int err;
> +
> +		err = start_background_scan(hdev);
> +		if (err)
> +			BT_ERR("Failed to start background scanning: err %d",
> +			       err);

Is it really worth to have the error here and not directly in start_background_scan.

> +	}
> +}
> +
> static void inquiry_complete(struct hci_dev *hdev, u8 status)
> {
> 	if (status) {
> diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
> index 0e329e5..d396e47 100644
> --- a/net/bluetooth/mgmt.c
> +++ b/net/bluetooth/mgmt.c
> @@ -4255,6 +4255,8 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
> 
> 	hci_dev_lock(hdev);
> 
> +	__hci_fixup_auto_conn(hdev);
> +
> 	mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
> 
> 	new_settings(hdev, match.sk);

Regards

Marcel



^ permalink raw reply

* Re: [RFC v2 08/15] Bluetooth: Move is_scan_and_conn_supported() to hci_core
From: Marcel Holtmann @ 2013-10-29 23:09 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-9-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch adds the "hci_" prefix and moves the is_scan_and_conn_
> supported() helper to hci_core so it can be reused in hci_core by
> the next patch.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> include/net/bluetooth/hci_core.h |  2 ++
> net/bluetooth/hci_conn.c         | 13 +------------
> net/bluetooth/hci_core.c         | 11 +++++++++++
> 3 files changed, 14 insertions(+), 12 deletions(-)
> 
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index e85b37e..0049036 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -782,6 +782,8 @@ int __hci_add_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> void __hci_remove_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> 				    u8 addr_type);
> 
> +bool hci_is_scan_and_conn_supported(struct hci_dev *hdev);
> +

as I said before, leave this all out until we figured out on how we handle the LE supported states. This patch now makes it clear that it is not fully thought through. And I am not moving this function around any time we need to change something.

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 07/15] Bluetooth: Introduce hdev->pending_auto_conn list
From: Marcel Holtmann @ 2013-10-29 23:08 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-8-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch introduces the hdev->pending_auto_conn list which holds
> the device addresses the kernel should autonomously connect. It also
> introduces some helper functions to manipulate the list.
> 
> The list and helper functions will be used by the next patch which
> implements the LE auto connection infrastructure.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> include/net/bluetooth/hci_core.h |  9 +++++
> net/bluetooth/hci_core.c         | 84 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 93 insertions(+)
> 
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index 64911aa..e85b37e 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -273,6 +273,8 @@ struct hci_dev {
> 
> 	struct list_head	conn_params;
> 
> +	struct list_head	pending_auto_conn;
> +

and more empty lines. Yeah. Seriously, this needs to stop.

Also this should not be named auto_conn since as I mentioned it before, this also makes sense to be used for one-shot connections triggered by connect().

> 	struct hci_dev_stats	stat;
> 
> 	atomic_t		promisc;
> @@ -773,6 +775,13 @@ struct hci_conn_params *hci_find_conn_params(struct hci_dev *hdev,
> 					   bdaddr_t *addr, u8 addr_type);
> void hci_conn_params_put(struct hci_conn_params *params);
> 
> +bool hci_has_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +			       u8 addr_type);
> +int __hci_add_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +				u8 addr_type);
> +void __hci_remove_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +				    u8 addr_type);
> +
> int hci_uuids_clear(struct hci_dev *hdev);
> 
> int hci_link_keys_clear(struct hci_dev *hdev);
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 0a278da..94c390b 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -2888,6 +2888,88 @@ static void __clear_conn_params(struct hci_dev *hdev)
> 		__remove_conn_params(params);
> }
> 
> +static struct bdaddr_list *__find_pending_auto_conn(struct hci_dev *hdev,
> +						    bdaddr_t *addr,
> +						    u8 addr_type)
> +{
> +	struct bdaddr_list *entry;
> +
> +	list_for_each_entry(entry, &hdev->pending_auto_conn, list) {
> +		if (bacmp(&entry->bdaddr, addr))
> +			continue;
> +		if (entry->bdaddr_type != addr_type)
> +			continue;

Why are we playing the continue trick here. Use positive matching and just return.

> +
> +		return entry;
> +	}
> +
> +	return NULL;
> +}
> +
> +bool hci_has_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +			       u8 addr_type)
> +{
> +	bool res;
> +
> +	hci_dev_lock(hdev);
> +
> +	if (__find_pending_auto_conn(hdev, addr, addr_type))
> +		res = true;
> +	else
> +		res = false;
> +
> +	hci_dev_unlock(hdev);
> +
> +	return res;
> +}
> +
> +/* This function requires the caller holds hdev->lock */
> +int __hci_add_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +				u8 addr_type)
> +{
> +	struct bdaddr_list *entry;
> +
> +	entry = __find_pending_auto_conn(hdev, addr, addr_type);
> +	if (entry)
> +		return 0;
> +
> +	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	bacpy(&entry->bdaddr, addr);
> +	entry->bdaddr_type = addr_type;
> +
> +	list_add(&entry->list, &hdev->pending_auto_conn);
> +
> +	return 0;
> +}
> +
> +/* This function requires the caller holds hdev->lock */
> +void __hci_remove_pending_auto_conn(struct hci_dev *hdev, bdaddr_t *addr,
> +				    u8 addr_type)
> +{
> +	struct bdaddr_list *entry;
> +
> +	entry = __find_pending_auto_conn(hdev, addr, addr_type);
> +	if (!entry)
> +		return;
> +
> +	list_del(&entry->list);
> +	kfree(entry);
> +}
> +
> +/* This function requires the caller holds hdev->lock */
> +static void __clear_pending_auto_conn(struct hci_dev *hdev)
> +{
> +	struct bdaddr_list *entry, *tmp;
> +
> +	list_for_each_entry_safe(entry, tmp, &hdev->pending_auto_conn, list) {
> +		list_del(&entry->list);
> +		kfree(entry);
> +	}
> +}
> +
> static void inquiry_complete(struct hci_dev *hdev, u8 status)
> {
> 	if (status) {
> @@ -2999,6 +3081,7 @@ struct hci_dev *hci_alloc_dev(void)
> 	INIT_LIST_HEAD(&hdev->long_term_keys);
> 	INIT_LIST_HEAD(&hdev->remote_oob_data);
> 	INIT_LIST_HEAD(&hdev->conn_params);
> +	INIT_LIST_HEAD(&hdev->pending_auto_conn);
> 	INIT_LIST_HEAD(&hdev->conn_hash.list);
> 
> 	INIT_WORK(&hdev->rx_work, hci_rx_work);
> @@ -3185,6 +3268,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
> 	hci_smp_ltks_clear(hdev);
> 	hci_remote_oob_data_clear(hdev);
> 	__clear_conn_params(hdev);
> +	__clear_pending_auto_conn(hdev);

All this naming does not make me happier.

Regards

Marcel


^ permalink raw reply

* Re: [RFC v2 06/15] Bluetooth: Use connection parameters if any
From: Marcel Holtmann @ 2013-10-29 23:04 UTC (permalink / raw)
  To: Andre Guedes; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1383053160-10175-7-git-send-email-andre.guedes@openbossa.org>

Hi Andre,

> This patch changes hci_connect_le() so it uses the connection
> parameters specified for the certain device. If no parameters
> were configured, we use the default values.
> 
> Signed-off-by: Andre Guedes <andre.guedes@openbossa.org>
> ---
> net/bluetooth/hci_conn.c | 13 +++++++++++--
> 1 file changed, 11 insertions(+), 2 deletions(-)
> 
> diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
> index 195b78f..075d070 100644
> --- a/net/bluetooth/hci_conn.c
> +++ b/net/bluetooth/hci_conn.c
> @@ -608,6 +608,7 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
> {
> 	struct hci_conn *conn;
> 	int err;
> +	struct hci_conn_params *params;

Put it above err variable.

> 
> 	if (test_bit(HCI_ADVERTISING, &hdev->flags))
> 		return ERR_PTR(-ENOTSUPP);
> @@ -652,8 +653,16 @@ static struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
> 	conn->sec_level = BT_SECURITY_LOW;
> 	conn->pending_sec_level = sec_level;
> 	conn->auth_type = auth_type;
> -	conn->conn_interval_min = hdev->le_conn_min_interval;
> -	conn->conn_interval_max = hdev->le_conn_max_interval;
> +
> +	params = hci_find_conn_params(hdev, &conn->dst, conn->dst_type);

Here is it now clear why the naming is horrible.

If you want to match with a put, then this needs to be hci_conn_params_get().

> +	if (params) {
> +		conn->conn_interval_min = params->conn_interval_min;
> +		conn->conn_interval_max = params->conn_interval_max;
> +		hci_conn_params_put(params);
> +	} else {
> +		conn->conn_interval_min = hdev->le_conn_min_interval;
> +		conn->conn_interval_max = hdev->le_conn_max_interval;
> +	}
> 
> 	err = hci_create_le_conn(conn);
> 	if (err)

Regards

Marcel


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox