Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH_v2 0/7] Initial code for hal hid host
From: Ravi kumar Veeramally @ 2013-10-23  7:41 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally

Patch set is all about initial code for hal hidhost interfaces which
are .virtual_unplug, .set_info, .get_protocol, .set_protocol,
.get_report, .set_report and .send_data. Also includes issue fixes
for .connect and .disconnect interfaces.
---
_v2 is about fixing issues as per Johan comments.

Ravi kumar Veeramally (7):
  android: Return right from hal_ipc_cmd call in hal hid connect
  android: Return right from hal_ipc_cmd call in hal hid disconnect
  android: Add initial code for hidhost virtual unplug
  android: Add initial code for hidhost get and set protocol
  android: Add initial code for hidhost get and set report
  android: Add initial code for hidhost send data
  android: Add initial code for hidhost set hid information

 android/hal-hidhost.c |  126 ++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 105 insertions(+), 21 deletions(-)

-- 
1.7.9.5


^ permalink raw reply

* [RFC 5/5] Bluetooth: Manually enable or disable 6LoWPAN between devices
From: Jukka Rissanen @ 2013-10-23  6:52 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1382511164-1989-1-git-send-email-jukka.rissanen@linux.intel.com>

This is a temporary patch where user can manually enable or
disable BT LE 6LoWPAN functionality between devices.
Eventually the connection is established automatically if
the devices are advertising suitable capability and this patch
can be removed.

If you have two devices with these BT addresses
        device1  00:11:22:33:44:55
        device2  66:77:88:99:00:11

First add the desired devices manually into kernel

root@dev1# echo 66:77:88:99:00:11 > /sys/kernel/debug/bluetooth/hci0/ble6lowpan
root@dev2# echo 00:11:22:33:44:55 > /sys/kernel/debug/bluetooth/hci0/ble6lowpan

then connect the devices

root@dev1# hciconfig hci0 leadv
root@dev2# hcitool lecc 00:11:22:33:44:55

if the connection is established, then you can send IPv6 packets
between these two systems using the link local addresses

root@dev1# ping6 fe80::6477:88ff:fe99:0011
root@dev2# ping6 fe80::211:22ff:fe33:4455

By default 6LoWPAN connection is not established between devices,
so you need to add the MAC addresses manually into the
/sys/kernel/debug/bluetooth/hci0/ble6lowpan file.
If you want to prevent further connections you can remove
MAC address from the debugfs file like this
echo "00:11:22:33:44:55 d" > /sys/kernel/debug/bluetooth/hci0/ble6lowpan
Rebooting or unloading the bluetooth kernel module will also clear the
settings from the kernel.
---
 net/bluetooth/ble_6lowpan.c | 164 ++++++++++++++++++++++++++++++++++++++++++++
 net/bluetooth/ble_6lowpan.h |   1 +
 net/bluetooth/hci_core.c    |   4 ++
 net/bluetooth/l2cap_core.c  |  12 ++--
 4 files changed, 174 insertions(+), 7 deletions(-)

diff --git a/net/bluetooth/ble_6lowpan.c b/net/bluetooth/ble_6lowpan.c
index 44d65b3..0baf211 100644
--- a/net/bluetooth/ble_6lowpan.c
+++ b/net/bluetooth/ble_6lowpan.c
@@ -12,6 +12,7 @@
 */
 
 #include <linux/version.h>
+#include <linux/debugfs.h>
 #include <linux/bitops.h>
 #include <linux/if_arp.h>
 #include <linux/netdevice.h>
@@ -1535,6 +1536,169 @@ static struct notifier_block ble_6lowpan_dev_notifier = {
 	.notifier_call = ble_6lowpan_device_event,
 };
 
+static LIST_HEAD(user_enabled);
+DEFINE_RWLOCK(user_enabled_list_lock);
+
+struct ble_6lowpan_enabled {
+	__u8 dev_name[HCI_MAX_NAME_LENGTH];
+	bdaddr_t addr;
+	struct list_head list;
+};
+
+bool ble_6lowpan_is_enabled(struct hci_dev *hdev, bdaddr_t *dst)
+{
+	struct ble_6lowpan_enabled *entry, *tmp;
+	bool found = false;
+
+	write_lock(&user_enabled_list_lock);
+	list_for_each_entry_safe(entry, tmp, &user_enabled, list) {
+		if (!strncmp(entry->dev_name, hdev->dev_name,
+				HCI_MAX_NAME_LENGTH) &&
+				!bacmp(dst, &entry->addr)) {
+			found = true;
+			break;
+		}
+	}
+	write_unlock(&user_enabled_list_lock);
+
+	/* Check also the device list just in case we removed the
+	 * device from debugfs before disconnecting.
+	 */
+	if (!found) {
+		struct ble_6lowpan_dev_record *entry, *tmp;
+		struct ble_6lowpan_dev_info *info;
+
+		write_lock(&net_dev_list_lock);
+		list_for_each_entry_safe(entry, tmp,
+					&ble_6lowpan_devices,
+					list) {
+			info = ble_6lowpan_dev_info(entry->dev);
+			if (info->conn->hcon->hdev == hdev &&
+						!bacmp(&info->addr, dst)) {
+				found = true;
+				break;
+			}
+		}
+		write_unlock(&net_dev_list_lock);
+	}
+
+	return found;
+}
+
+static int ble_6lowpan_debugfs_show(struct seq_file *f, void *p)
+{
+	struct ble_6lowpan_enabled *entry, *tmp;
+
+	write_lock(&user_enabled_list_lock);
+	list_for_each_entry_safe(entry, tmp, &user_enabled, list)
+		seq_printf(f, "%pMR\n", &entry->addr);
+
+	write_unlock(&user_enabled_list_lock);
+
+	return 0;
+}
+
+static int ble_6lowpan_debugfs_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ble_6lowpan_debugfs_show, inode->i_private);
+}
+
+static ssize_t ble_6lowpan_writer(struct file *fp,
+				const char __user *user_buffer,
+                                size_t count, loff_t *position)
+{
+#define MAC_STR_LEN 17
+	char mac_buf[MAC_STR_LEN + 1];
+	ssize_t ret;
+	bool delete_mode = false;
+
+        if(count > (MAC_STR_LEN + 1))
+		delete_mode = true;
+	else if (count < MAC_STR_LEN)
+		return count;
+
+	BT_DBG("count %zd mode %d", count, delete_mode);
+
+	memset(mac_buf, 0, MAC_STR_LEN + 1);
+        ret = simple_write_to_buffer(mac_buf, MAC_STR_LEN, position,
+							user_buffer, count);
+	if (ret > 0) {
+		struct ble_6lowpan_enabled *entry = NULL, *tmp;
+		bdaddr_t bdaddr;
+
+		if (sscanf(mac_buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx",
+				&bdaddr.b[5], &bdaddr.b[4],
+				&bdaddr.b[3], &bdaddr.b[2],
+				&bdaddr.b[1], &bdaddr.b[0]) != 6)
+			return -EINVAL;
+
+		BT_DBG("user %pMR", &bdaddr);
+
+		write_lock(&user_enabled_list_lock);
+		list_for_each_entry_safe(entry, tmp, &user_enabled, list) {
+			if (!bacmp(&entry->addr, &bdaddr)) {
+				struct hci_dev *hdev = fp->f_inode->i_private;
+
+				if (!strncmp(entry->dev_name, hdev->dev_name,
+						HCI_MAX_NAME_LENGTH) &&
+								delete_mode) {
+					break;
+				} else {
+					ret = -EEXIST;
+					break;
+				}
+			}
+		}
+		write_unlock(&user_enabled_list_lock);
+
+		if (ret > 0) {
+			struct hci_dev *hdev = fp->f_inode->i_private;
+
+			if (delete_mode) {
+				write_lock(&user_enabled_list_lock);
+				list_del(&entry->list);
+				kfree(entry);
+				write_unlock(&user_enabled_list_lock);
+			} else {
+				entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+				if (!entry)
+					return -ENOMEM;
+
+				strncpy(entry->dev_name, hdev->dev_name,
+					HCI_MAX_NAME_LENGTH);
+				entry->addr = bdaddr;
+
+				write_lock(&user_enabled_list_lock);
+				INIT_LIST_HEAD(&entry->list);
+				list_add(&entry->list, &user_enabled);
+				write_unlock(&user_enabled_list_lock);
+			}
+		}
+	}
+
+	return ret;
+}
+
+static const struct file_operations ble_6lowpan_debugfs_fops = {
+	.open		= ble_6lowpan_debugfs_open,
+	.read		= seq_read,
+	.write		= ble_6lowpan_writer,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static struct dentry *ble_6lowpan_debugfs;
+
+void ble_6lowpan_add_debugfs(struct hci_dev *hdev)
+{
+	if (hdev->debugfs) {
+		ble_6lowpan_debugfs = debugfs_create_file("ble6lowpan", 0644,
+			hdev->debugfs, hdev, &ble_6lowpan_debugfs_fops);
+		if (!ble_6lowpan_debugfs)
+			BT_ERR("Failed to create 6LoWPAN debug file");
+	}
+}
+
 int ble_6lowpan_init(void)
 {
 	int err;
diff --git a/net/bluetooth/ble_6lowpan.h b/net/bluetooth/ble_6lowpan.h
index 7975a55..047b8b7 100644
--- a/net/bluetooth/ble_6lowpan.h
+++ b/net/bluetooth/ble_6lowpan.h
@@ -23,5 +23,6 @@ int ble_6lowpan_del_conn(struct l2cap_conn *conn);
 int ble_6lowpan_init(void);
 void ble_6lowpan_cleanup(void);
 bool ble_6lowpan_is_enabled(struct hci_dev *hdev, bdaddr_t *dst);
+void ble_6lowpan_add_debugfs(struct hci_dev *hdev);
 
 #endif /* __BLE_LOWPAN_H */
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 6ccc4eb..d0db818 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -34,6 +34,8 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 
+#include "ble_6lowpan.h"
+
 static void hci_rx_work(struct work_struct *work);
 static void hci_cmd_work(struct work_struct *work);
 static void hci_tx_work(struct work_struct *work);
@@ -1406,6 +1408,8 @@ static int __hci_init(struct hci_dev *hdev)
 				    hdev, &conn_max_interval_fops);
 	}
 
+	ble_6lowpan_add_debugfs(hdev);
+
 	return 0;
 }
 
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index fb1a49c..e3b30dd 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -6542,11 +6542,6 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
 	return exact ? lm1 : lm2;
 }
 
-static bool is_ble_6lowpan(void)
-{
-	return false;
-}
-
 void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 {
 	struct l2cap_conn *conn;
@@ -6558,7 +6553,9 @@ void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 		if (conn) {
 			l2cap_conn_ready(conn);
 
-			if (hcon->type == LE_LINK && is_ble_6lowpan())
+			if (hcon->type == LE_LINK &&
+					ble_6lowpan_is_enabled(hcon->hdev,
+								&hcon->dst))
 				ble_6lowpan_add_conn(conn);
 		}
 	} else {
@@ -6581,7 +6578,8 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
 	BT_DBG("hcon %p reason %d", hcon, reason);
 
-	if (hcon->type == LE_LINK && is_ble_6lowpan())
+	if (hcon->type == LE_LINK && ble_6lowpan_is_enabled(hcon->hdev,
+							&hcon->dst))
 		ble_6lowpan_del_conn(hcon->l2cap_data);
 
 	l2cap_conn_del(hcon, bt_to_errno(reason));
-- 
1.7.11.7


^ permalink raw reply related

* [RFC 4/5] Bluetooth: Set route to peer for 6LoWPAN
From: Jukka Rissanen @ 2013-10-23  6:52 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1382511164-1989-1-git-send-email-jukka.rissanen@linux.intel.com>

---
 net/bluetooth/ble_6lowpan.c | 36 ++++++++++++++++++++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/net/bluetooth/ble_6lowpan.c b/net/bluetooth/ble_6lowpan.c
index 5b3ebe1..44d65b3 100644
--- a/net/bluetooth/ble_6lowpan.c
+++ b/net/bluetooth/ble_6lowpan.c
@@ -1344,6 +1344,39 @@ static void set_addr(u8 *eui, u8 *addr)
 	eui[0] ^= 2;
 }
 
+static int add_peer_route(struct ble_6lowpan_dev_info *dev,
+			struct net_device *net, bdaddr_t *addr)
+{
+	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);
+
+	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 set_dev_addr(struct net_device *net, bdaddr_t *addr)
 {
 	net->addr_assign_type = NET_ADDR_PERM;
@@ -1428,6 +1461,9 @@ int ble_6lowpan_add_conn(struct l2cap_conn *conn)
 		write_unlock(&net_dev_list_lock);
 
 		ifup(net);
+
+		if ((status = add_peer_route(dev, net, &dev->addr)) < 0)
+			BT_INFO("Setting route failed %d", status);
 	}
 
 	return 0;
-- 
1.7.11.7


^ permalink raw reply related

* [RFC 3/5] route: Exporting ip6_route_add() so that BLE 6LoWPAN can use it
From: Jukka Rissanen @ 2013-10-23  6:52 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1382511164-1989-1-git-send-email-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 2/5] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Jukka Rissanen @ 2013-10-23  6:52 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1382511164-1989-1-git-send-email-jukka.rissanen@linux.intel.com>

---
 net/bluetooth/ble_6lowpan.c | 1114 ++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 1112 insertions(+), 2 deletions(-)

diff --git a/net/bluetooth/ble_6lowpan.c b/net/bluetooth/ble_6lowpan.c
index 0fd3302..5b3ebe1 100644
--- a/net/bluetooth/ble_6lowpan.c
+++ b/net/bluetooth/ble_6lowpan.c
@@ -69,6 +69,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 +113,723 @@ static inline void ble_6lowpan_raw_dump_table(const char *caller, char *msg,
 #endif /* DEBUG */
 }
 
+static inline bool ble_6lowpan_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 ble_6lowpan_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 = ble_6lowpan_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 = ble_6lowpan_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 = ble_6lowpan_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;
+	}
+
+	ble_6lowpan_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 ble_6lowpan_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;
+	}
+
+	ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_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;
+
+	ble_6lowpan_raw_dump_table(__func__,
+				"raw skb data dump before receiving",
+				new->data, new->len);
+
+	stat = ble_6lowpan_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 *ble_6lowpan_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 = ble_6lowpan_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 = ble_6lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+		fail |= ble_6lowpan_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 = ble_6lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+		fail |= ble_6lowpan_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 = ble_6lowpan_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;
+	}
+
+	ble_6lowpan_raw_dump_inline(NULL,
+				"Reconstructed ipv6 multicast addr is",
+				ipaddr->s6_addr, 16);
+
+	return 0;
+}
+
+static int
+ble_6lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
+{
+	u8 tmp;
+
+	if (!uh)
+		goto err;
+
+	if (ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_dev_info *info;
+	int err;
+
+	ble_6lowpan_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 (ble_6lowpan_fetch_skb_u8(skb, &iphc0))
+		goto drop;
+
+	info = ble_6lowpan_dev_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 (ble_6lowpan_fetch_skb_u8(skb, &slen) || /* frame length */
+		    ble_6lowpan_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 (ble_6lowpan_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 = ble_6lowpan_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 (ble_6lowpan_fetch_skb_u8(skb, &iphc0))
+				goto drop;
+
+			break;
+		}
+		spin_unlock_bh(&flist_lock);
+
+		return kfree_skb(skb), 0;
+	}
+	default:
+		break;
+	}
+
+	if (ble_6lowpan_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 (ble_6lowpan_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 (ble_6lowpan_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 (ble_6lowpan_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 (ble_6lowpan_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 (ble_6lowpan_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 (ble_6lowpan_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 = ble_6lowpan_uncompress_context_based_src_addr(
+				skb, &hdr.saddr, tmp);
+	} else {
+		/* Source address uncompression */
+		pr_debug("source address stateless compression\n");
+		err = ble_6lowpan_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 = ble_6lowpan_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 (ble_6lowpan_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));
+
+		ble_6lowpan_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);
+
+	ble_6lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+							sizeof(hdr));
+
+	return ble_6lowpan_skb_deliver(skb, &hdr, dev);
+
+unlock_and_drop:
+	spin_unlock_bh(&flist_lock);
+drop:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
 static int ble_6lowpan_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_IEEE802154)
+		goto drop;
+
+	ble_6lowpan_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 (ble_6lowpan_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 (ble_6lowpan_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 +865,406 @@ static void ble_6lowpan_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;
+	hci_send_acl(conn->hchan, skb, ACL_START);
+}
+
+static inline int ble_6lowpan_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++;
+
+	ble_6lowpan_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);
+
+		ble_6lowpan_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 *ble_6lowpan_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 = ble_6lowpan_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 lowpan_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 = ble_6lowpan_create_pdu(conn, msg, len, priority, dev);
+	if (IS_ERR(skb))
+		return -EINVAL;
 
 	ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_dev_info *info;
+
+	if (type != ETH_P_IPV6)
+		return -EINVAL;
+
+	info = ble_6lowpan_dev_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);
+
+	ble_6lowpan_raw_dump_table(__func__, "raw skb network header dump",
+		skb_network_header(skb), sizeof(struct ipv6hdr));
+
+	ble_6lowpan_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 */
+
+	ble_6lowpan_raw_dump_inline(__func__, "daddr",
+			(unsigned char *)daddr, 8);
+
+	ble_6lowpan_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)
+		ble_6lowpan_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);
+
+	ble_6lowpan_raw_dump_table(__func__, "raw skb data dump compressed",
+				skb->data, skb->len);
+
+	return 0;
+}
+
 /* Packet to BT LE device */
 static int ble_6lowpan_send(struct l2cap_conn *conn, const void *saddr,
 			const void *daddr, struct sk_buff *skb,
@@ -199,6 +1304,10 @@ static const struct net_device_ops ble_6lowpan_netdev_ops = {
 	.ndo_start_xmit		= ble_6lowpan_xmit,
 };
 
+static struct header_ops ble_6lowpan_header_ops = {
+	.create	= ble_6lowpan_header_create,
+};
+
 static void ble_6lowpan_setup(struct net_device *dev)
 {
 	dev->addr_len		= IEEE802154_ADDR_LEN;
@@ -212,6 +1321,7 @@ static void ble_6lowpan_setup(struct net_device *dev)
 	dev->watchdog_timeo	= 0;
 
 	dev->netdev_ops		= &ble_6lowpan_netdev_ops;
+	dev->header_ops		= &ble_6lowpan_header_ops;
 	dev->destructor		= free_netdev;
 }
 
-- 
1.7.11.7


^ permalink raw reply related

* [RFC 1/5] Bluetooth: Initial skeleton code for BT LE 6LoWPAN
From: Jukka Rissanen @ 2013-10-23  6:52 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1382511164-1989-1-git-send-email-jukka.rissanen@linux.intel.com>

---
 include/net/bluetooth/l2cap.h |   1 +
 net/bluetooth/Makefile        |   2 +-
 net/bluetooth/ble_6lowpan.c   | 404 ++++++++++++++++++++++++++++++++++++++++++
 net/bluetooth/ble_6lowpan.h   |  27 +++
 net/bluetooth/l2cap_core.c    |  22 ++-
 5 files changed, 454 insertions(+), 2 deletions(-)
 create mode 100644 net/bluetooth/ble_6lowpan.c
 create mode 100644 net/bluetooth/ble_6lowpan.h

diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index 3d922b9..645cd30 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/Makefile b/net/bluetooth/Makefile
index 6a791e7..a70625b 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 ble_6lowpan.o
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/ble_6lowpan.c b/net/bluetooth/ble_6lowpan.c
new file mode 100644
index 0000000..0fd3302
--- /dev/null
+++ b/net/bluetooth/ble_6lowpan.c
@@ -0,0 +1,404 @@
+/*
+   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 "ble6lowpan%d"
+
+/*
+ * The devices list contains those devices that we are acting
+ * as a proxy. The BT LE 6LoWPAN device is a virtual device that
+ * connects to the Bluetooth LE device. The real connection to
+ * BT LE device is done via l2cap layer. There exists one
+ * virtual device / one BT LE 6LoWPAN device. The list contains
+ * struct ble_6lowpan_dev_record elements.
+ */
+static LIST_HEAD(ble_6lowpan_devices);
+DEFINE_RWLOCK(net_dev_list_lock);
+
+struct ble_6lowpan_dev_record {
+	struct net_device *dev;
+	struct delayed_work delete_timer;
+	struct list_head list;
+};
+
+struct ble_6lowpan_dev_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
+ble_6lowpan_dev_info *ble_6lowpan_dev_info(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+
+/* print data in line */
+static inline void ble_6lowpan_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 ble_6lowpan_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 ble_6lowpan_recv_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+	kfree_skb(skb);
+	return NET_RX_DROP;
+}
+
+/* Packet from BT LE device */
+int ble_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct ble_6lowpan_dev_record *entry, *tmp;
+	struct net_device *dev = NULL;
+	int status = -ENOENT;
+
+	write_lock(&net_dev_list_lock);
+
+	list_for_each_entry_safe(entry, tmp, &ble_6lowpan_devices, list) {
+		if (ble_6lowpan_dev_info(entry->dev)->conn == conn) {
+			dev = ble_6lowpan_dev_info(entry->dev)->net;
+			break;
+		}
+	}
+
+	write_unlock(&net_dev_list_lock);
+
+	if (dev) {
+		status = ble_6lowpan_recv_pkt(skb, dev);
+		BT_DBG("recv pkt %d", status);
+	}
+
+	return status;
+}
+
+static void ble_6lowpan_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 lowpan_conn_send(struct l2cap_conn *conn,
+			void *msg, size_t len, u32 priority,
+			struct net_device *dev)
+{
+	struct sk_buff *skb = {0};
+
+	ble_6lowpan_do_send(conn, skb);
+	return 0;
+}
+
+/* Packet to BT LE device */
+static int ble_6lowpan_send(struct l2cap_conn *conn, const void *saddr,
+			const void *daddr, struct sk_buff *skb,
+			struct net_device *dev)
+{
+	ble_6lowpan_raw_dump_table(__func__,
+				"raw skb data dump before fragmentation",
+				skb->data, skb->len);
+
+	return lowpan_conn_send(conn, skb->data, skb->len, 0, dev);
+}
+
+static netdev_tx_t ble_6lowpan_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	int err = -1;
+
+	pr_debug("ble 6lowpan packet xmit\n");
+
+	if (ble_6lowpan_dev_info(dev)->conn)
+		err = ble_6lowpan_send(ble_6lowpan_dev_info(dev)->conn,
+				dev->dev_addr,
+				&ble_6lowpan_dev_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 ble_6lowpan_netdev_ops = {
+	.ndo_start_xmit		= ble_6lowpan_xmit,
+};
+
+static void ble_6lowpan_setup(struct net_device *dev)
+{
+	dev->addr_len		= IEEE802154_ADDR_LEN;
+	dev->type		= ARPHRD_IEEE802154;
+
+	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		= &ble_6lowpan_netdev_ops;
+	dev->destructor		= free_netdev;
+}
+
+static struct device_type ble_type = {
+	.name	= "ble6lowpan",
+};
+
+static void set_addr(u8 *eui, u8 *addr)
+{
+	/* 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;
+}
+
+static void set_dev_addr(struct net_device *net, bdaddr_t *addr)
+{
+	net->addr_assign_type = NET_ADDR_PERM;
+	set_addr(net->dev_addr, addr->b);
+	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 ble_6lowpan_add_conn(struct l2cap_conn *conn)
+{
+	struct net_device *net;
+	struct ble_6lowpan_dev_info *dev;
+	struct ble_6lowpan_dev_record *entry;
+	int status;
+
+	net = alloc_netdev(sizeof(struct ble_6lowpan_dev_info),
+			IFACE_NAME_TEMPLATE, ble_6lowpan_setup);
+	if (!net)
+		return -ENOMEM;
+
+	dev = netdev_priv(net);
+	dev->net = net;
+
+	memcpy(&dev->myaddr, &conn->hcon->hdev->bdaddr, sizeof(bdaddr_t));
+	memcpy(&dev->addr, &conn->hcon->dst, sizeof(bdaddr_t));
+
+	set_dev_addr(net, &dev->myaddr);
+
+	dev->conn = conn;
+
+	net->netdev_ops = &ble_6lowpan_netdev_ops;
+	SET_NETDEV_DEV(net, &conn->hcon->dev);
+	SET_NETDEV_DEVTYPE(net, &ble_type);
+
+	status = register_netdev(net);
+	if (status < 0) {
+		BT_INFO("register_netdev failed %d", status);
+		free_netdev(net);
+		return status;
+	} else {
+		struct inet6_dev *idev;
+
+		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 ble_6lowpan_dev_record),
+				GFP_KERNEL);
+		if (!entry)
+			return -ENOMEM;
+
+		entry->dev = net;
+
+		write_lock(&net_dev_list_lock);
+		INIT_LIST_HEAD(&entry->list);
+		list_add(&entry->list, &ble_6lowpan_devices);
+		write_unlock(&net_dev_list_lock);
+
+		ifup(net);
+	}
+
+	return 0;
+}
+
+static void delete_timeout(struct work_struct *work)
+{
+	struct ble_6lowpan_dev_record *entry = container_of(work,
+						struct ble_6lowpan_dev_record,
+						delete_timer.work);
+
+	unregister_netdev(entry->dev);
+	kfree(entry);
+}
+
+int ble_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+	struct ble_6lowpan_dev_record *entry, *tmp;
+	int status = -ENOENT;
+
+	write_lock(&net_dev_list_lock);
+
+	list_for_each_entry_safe(entry, tmp, &ble_6lowpan_devices, list) {
+		if (ble_6lowpan_dev_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 ble_6lowpan_device_event(struct notifier_block *unused,
+				unsigned long event, void *ptr)
+{
+	struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+	struct ble_6lowpan_dev_record *entry, *tmp;
+
+	if (dev->type == ARPHRD_IEEE802154) {
+		switch (event) {
+		case NETDEV_UNREGISTER:
+			write_lock(&net_dev_list_lock);
+			list_for_each_entry_safe(entry, tmp,
+						&ble_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 ble_6lowpan_dev_notifier = {
+	.notifier_call = ble_6lowpan_device_event,
+};
+
+int ble_6lowpan_init(void)
+{
+	int err;
+
+	err = register_netdevice_notifier(&ble_6lowpan_dev_notifier);
+
+	return err;
+}
+
+void ble_6lowpan_cleanup(void)
+{
+	unregister_netdevice_notifier(&ble_6lowpan_dev_notifier);
+}
diff --git a/net/bluetooth/ble_6lowpan.h b/net/bluetooth/ble_6lowpan.h
new file mode 100644
index 0000000..7975a55
--- /dev/null
+++ b/net/bluetooth/ble_6lowpan.h
@@ -0,0 +1,27 @@
+/*
+   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 __BLE_LOWPAN_H
+#define __BLE_LOWPAN_H
+
+#include <linux/skbuff.h>
+#include <net/bluetooth/l2cap.h>
+
+int ble_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb);
+int ble_6lowpan_add_conn(struct l2cap_conn *conn);
+int ble_6lowpan_del_conn(struct l2cap_conn *conn);
+int ble_6lowpan_init(void);
+void ble_6lowpan_cleanup(void);
+bool ble_6lowpan_is_enabled(struct hci_dev *hdev, bdaddr_t *dst);
+
+#endif /* __BLE_LOWPAN_H */
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index d52bd0d..fb1a49c 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 "ble_6lowpan.h"
 
 bool disable_ertm;
 
@@ -6500,6 +6501,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:
+		ble_6lowpan_recv(conn, skb);
+		break;
+
 	default:
 		l2cap_data_channel(conn, cid, skb);
 		break;
@@ -6537,6 +6542,11 @@ int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr)
 	return exact ? lm1 : lm2;
 }
 
+static bool is_ble_6lowpan(void)
+{
+	return false;
+}
+
 void l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
 {
 	struct l2cap_conn *conn;
@@ -6545,8 +6555,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_ble_6lowpan())
+				ble_6lowpan_add_conn(conn);
+		}
 	} else {
 		l2cap_conn_del(hcon, bt_to_errno(status));
 	}
@@ -6567,6 +6581,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_ble_6lowpan())
+		ble_6lowpan_del_conn(hcon->l2cap_data);
+
 	l2cap_conn_del(hcon, bt_to_errno(reason));
 }
 
@@ -6849,11 +6866,14 @@ int __init l2cap_init(void)
 	l2cap_debugfs = debugfs_create_file("l2cap", 0444, bt_debugfs,
 					    NULL, &l2cap_debugfs_fops);
 
+	ble_6lowpan_init();
+
 	return 0;
 }
 
 void l2cap_exit(void)
 {
+	ble_6lowpan_cleanup();
 	debugfs_remove(l2cap_debugfs);
 	l2cap_cleanup_sockets();
 }
-- 
1.7.11.7


^ permalink raw reply related

* [RFC 0/5] Bluetooth LE 6LoWPAN
From: Jukka Rissanen @ 2013-10-23  6:52 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

The code is not yet ready for prime time but is sent
in order to get discussion started.

The devices need to be setup manually in this version.
This is implemented in patch 5. That patch is just a
temporary one and will be eventually removed when the
devices can discover the 6LoWPAN service automatically.

Test usage:
You have two devices with BlueZ 5 stack in them
     device1  00:11:22:33:44:55
     device2  66:77:88:99:00:11

First add the desired devices manually into kernel
root@device1# echo 66:77:88:99:00:11 > /sys/kernel/debug/bluetooth/hci0/ble6lowpan
root@device2# echo 00:11:22:33:44:55 > /sys/kernel/debug/bluetooth/hci0/ble6lowpan

then connect the devices
root@device1# hciconfig hci0 leadv
root@device2# hcitool lecc 00:11:22:33:44:55

if the connection is established, then you can send IPv6 packets
between these two systems using the link local addresses

root@device1# ping6 fe80::6477:88ff:fe99:0011
root@device2# ping6 fe80::211:22ff:fe33:4455

the ble6lowpan0 interface is created and routes to peer are
automatically setup.

By default 6LoWPAN connection is not established between devices,
so you need to add the MAC addresses manually into the
/sys/kernel/debug/bluetooth/hci0/ble6lowpan file.
If you want to prevent further connections you can remove
MAC address from the debugfs file like this
echo "00:11:22:33:44:55 d" > /sys/kernel/debug/bluetooth/hci0/ble6lowpan
Rebooting or unloading the bluetooth kernel module will also clear the
settings from the kernel.


Areas of improvement:
- route setting could be done in user space
- ARP type is set to IEEE802154 but the transferred packets are actually
  not that type
- discovery of 6LoWPAN service needs be automatic
- split patches more, especially number 2 is a bit too big


Cheers,
Jukka


Jukka Rissanen (5):
  Bluetooth: Initial skeleton code for BT LE 6LoWPAN
  Bluetooth: Enable 6LoWPAN support for BT LE devices
  route: Exporting ip6_route_add() so that BLE 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 +
 net/bluetooth/Makefile        |    2 +-
 net/bluetooth/ble_6lowpan.c   | 1714 +++++++++++++++++++++++++++++++++++++++++
 net/bluetooth/ble_6lowpan.h   |   28 +
 net/bluetooth/hci_core.c      |    4 +
 net/bluetooth/l2cap_core.c    |   20 +-
 net/ipv6/route.c              |    1 +
 7 files changed, 1768 insertions(+), 2 deletions(-)
 create mode 100644 net/bluetooth/ble_6lowpan.c
 create mode 100644 net/bluetooth/ble_6lowpan.h

-- 
1.7.11.7


^ permalink raw reply

* Re: [PATCH] android: Fix build error for Android
From: Johan Hedberg @ 2013-10-23  6:40 UTC (permalink / raw)
  To: Lukasz Rymanowski; +Cc: linux-bluetooth, andrei.emeltchenko, szymon.janc
In-Reply-To: <1382482413-9324-1-git-send-email-lukasz.rymanowski@tieto.com>

Hi Lukasz,

On Wed, Oct 23, 2013, Lukasz Rymanowski wrote:
> ---
>  android/main.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/android/main.c b/android/main.c
> index 988d08b..606baf2 100644
> --- a/android/main.c
> +++ b/android/main.c
> @@ -52,6 +52,10 @@
>  #include "hal-msg.h"
>  #include "ipc.h"
>  
> +#if defined(ANDROID)
> +#include <sys/capability.h>
> +#endif
> +
>  static GMainLoop *event_loop;
>  static struct mgmt *mgmt_if = NULL;

You're going to have to do a lot more explaining for this one.
Firstly, you should quote the error you get in the commit message.
Secondly, the patch doesn't seem to agree with the subject since the
conditional you're adding shouldn't change anything for the android
build. Thirdly, if there are some quirks with Android (bionic) includes
there should be a proper code comment explaining this so that the piece
of code doesn't by mistake get removed in the future.

Johan

^ permalink raw reply

* [PATCH] android: Fix build error for Android
From: Lukasz Rymanowski @ 2013-10-22 22:53 UTC (permalink / raw)
  To: linux-bluetooth
  Cc: andrei.emeltchenko, johan.hedberg, szymon.janc, Lukasz Rymanowski

---
 android/main.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/android/main.c b/android/main.c
index 988d08b..606baf2 100644
--- a/android/main.c
+++ b/android/main.c
@@ -52,6 +52,10 @@
 #include "hal-msg.h"
 #include "ipc.h"
 
+#if defined(ANDROID)
+#include <sys/capability.h>
+#endif
+
 static GMainLoop *event_loop;
 static struct mgmt *mgmt_if = NULL;
 
-- 
1.8.4


^ permalink raw reply related

* Re: [PATCH 1/6] android: Handle mgmt changed events
From: Johan Hedberg @ 2013-10-22 20:23 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth, Andrei Emeltchenko
In-Reply-To: <1382460476-6604-1-git-send-email-szymon.janc@tieto.com>

Hi Szymon,

On Tue, Oct 22, 2013, Szymon Janc wrote:
> Add code handling changing adapter settings.
> ---
>  android/adapter.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 129 insertions(+)

All patches have been applied, but I merged patch 5 into patch 2 before
that. Two things you should keep in mind about this:

1. It's not ok to introduce a bug in one patch and then fix it in
another when both patches are part of the same upstream submission (i.e.
just don't introduce the patch to begin with if you've already got a fix
for it).

2. Learn about git rebase -i and the "fixup" keyword. Fixing this issue
was extremely trivial using that.

Johan

^ permalink raw reply

* Re: [PATCH 1/5] android: Add initial code for hidhost virtual unplug
From: Johan Hedberg @ 2013-10-22 20:16 UTC (permalink / raw)
  To: Ravi kumar Veeramally; +Cc: linux-bluetooth
In-Reply-To: <1382467793-4709-1-git-send-email-ravikumar.veeramally@linux.intel.com>

Hi Ravi,

On Tue, Oct 22, 2013, Ravi kumar Veeramally wrote:
> @@ -87,7 +89,13 @@ static bt_status_t hh_virtual_unplug(bt_bdaddr_t *bd_addr)
>  	if (!bd_addr)
>  		return BT_STATUS_PARM_INVALID;
>  
> -	return BT_STATUS_UNSUPPORTED;
> +	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
> +
> +	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_VP,
> +			sizeof(cmd), &cmd, 0, NULL, NULL) < 0)
> +		return BT_STATUS_FAIL;
> +
> +	return BT_STATUS_SUCCESS;
>  }

It seems you haven't quite paid attention to what happened in the
upstream tree today. Take a look at commit 039369d809dedcdfd3c09fb

Basically you should be able to simply do "return hal_ipc_cmd(...);"
in most cases now.

Johan

^ permalink raw reply

* [PATCH 5/5] android: Add initial code for hidhost set hid information
From: Ravi kumar Veeramally @ 2013-10-22 18:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1382467793-4709-1-git-send-email-ravikumar.veeramally@linux.intel.com>

This adds the initial code for hidhost .set_info interface
---
 android/hal-hidhost.c |   20 +++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index a0349b2..aeebc6c 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -100,6 +100,8 @@ static bt_status_t hh_virtual_unplug(bt_bdaddr_t *bd_addr)
 
 static bt_status_t hh_set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
 {
+	struct hal_msg_cmd_bt_hid_set_info cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -108,7 +110,23 @@ static bt_status_t hh_set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
 	if (!bd_addr)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.attr = hid_info.attr_mask;
+	cmd.subclass = hid_info.sub_class;
+	cmd.app_id = hid_info.app_id;
+	cmd.vendor = hid_info.vendor_id;
+	cmd.product = hid_info.product_id;
+	cmd.country = hid_info.ctry_code;
+	cmd.descr_len = hid_info.dl_len;
+	memcpy(cmd.descr, hid_info.dsc_list, cmd.descr_len);
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_SET_INFO,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0) {
+		error("Failed to set hid info on device");
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_get_protocol(bt_bdaddr_t *bd_addr,
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 4/5] android: Add initial code for hidhost send data
From: Ravi kumar Veeramally @ 2013-10-22 18:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1382467793-4709-1-git-send-email-ravikumar.veeramally@linux.intel.com>

This adds the initial code for hidhost .send_data interface
---
 android/hal-hidhost.c |   12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 5a4614d..a0349b2 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -261,6 +261,8 @@ static bt_status_t hh_set_report(bt_bdaddr_t *bd_addr,
 
 static bt_status_t hh_send_data(bt_bdaddr_t *bd_addr, char *data)
 {
+	struct hal_msg_cmd_bt_hid_send_data cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -269,7 +271,15 @@ static bt_status_t hh_send_data(bt_bdaddr_t *bd_addr, char *data)
 	if (!bd_addr || !data)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_SEND_DATA,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0) {
+		error("Failed to send data to hid devie");
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_init(bthh_callbacks_t *callbacks)
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 3/5] android: Add initial code for hidhost get and set report
From: Ravi kumar Veeramally @ 2013-10-22 18:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1382467793-4709-1-git-send-email-ravikumar.veeramally@linux.intel.com>

This adds the initial code for hidhost .get_report and .set_report
interfaces
---
 android/hal-hidhost.c |   49 +++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 47 insertions(+), 2 deletions(-)

diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 506d721..5a4614d 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -188,6 +188,8 @@ static bt_status_t hh_get_report(bt_bdaddr_t *bd_addr,
 						uint8_t reportId,
 						int bufferSize)
 {
+	struct hal_msg_cmd_bt_hid_get_report cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -196,13 +198,36 @@ static bt_status_t hh_get_report(bt_bdaddr_t *bd_addr,
 	if (!bd_addr)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+	cmd.id = reportId;
+
+	switch (reportType) {
+	case BTHH_INPUT_REPORT:
+		cmd.type = HAL_MSG_BT_HID_INPUT_REPORT;
+		break;
+	case BTHH_OUTPUT_REPORT:
+		cmd.type = HAL_MSG_BT_HID_OUTPUT_REPORT;
+		break;
+	case BTHH_FEATURE_REPORT:
+		cmd.type = HAL_MSG_BT_HID_FEATURE_REPORT;
+		break;
+	}
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_GET_REPORT,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0) {
+		error("Failed to get hid devie report");
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_set_report(bt_bdaddr_t *bd_addr,
 						bthh_report_type_t reportType,
 						char *report)
 {
+	struct hal_msg_cmd_bt_hid_set_report cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -211,7 +236,27 @@ static bt_status_t hh_set_report(bt_bdaddr_t *bd_addr,
 	if (!bd_addr || !report)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	switch (reportType) {
+	case BTHH_INPUT_REPORT:
+		cmd.type = HAL_MSG_BT_HID_INPUT_REPORT;
+		break;
+	case BTHH_OUTPUT_REPORT:
+		cmd.type = HAL_MSG_BT_HID_OUTPUT_REPORT;
+		break;
+	case BTHH_FEATURE_REPORT:
+		cmd.type = HAL_MSG_BT_HID_FEATURE_REPORT;
+		break;
+	}
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_SET_REPORT,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0) {
+		error("Failed to set hid devie report");
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_send_data(bt_bdaddr_t *bd_addr, char *data)
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 2/5] android: Add initial code for hidhost get and set protocol
From: Ravi kumar Veeramally @ 2013-10-22 18:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1382467793-4709-1-git-send-email-ravikumar.veeramally@linux.intel.com>

This adds the initial code for hidhost .get_protocol and .set_protocol
interfaces
---
 android/hal-hidhost.c |   48 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 46 insertions(+), 2 deletions(-)

diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index 523ce6d..506d721 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -114,6 +114,8 @@ static bt_status_t hh_set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
 static bt_status_t hh_get_protocol(bt_bdaddr_t *bd_addr,
 					bthh_protocol_mode_t protocolMode)
 {
+	struct hal_msg_cmd_bt_hid_get_protocol cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -122,12 +124,34 @@ static bt_status_t hh_get_protocol(bt_bdaddr_t *bd_addr,
 	if (!bd_addr)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	switch (protocolMode) {
+	case BTHH_REPORT_MODE:
+		cmd.mode = HAL_MSG_BT_HID_REPORT_PROTOCOL;
+		break;
+	case BTHH_BOOT_MODE:
+		cmd.mode = HAL_MSG_BT_HID_BOOT_PROTOCOL;
+		break;
+	case BTHH_UNSUPPORTED_MODE:
+		cmd.mode = HAL_MSG_BT_HID_UNSUPPORTED_PROTOCOL;
+		break;
+	}
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_GET_PROTOCOL,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0) {
+		error("Failed to get hid device protocol");
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_set_protocol(bt_bdaddr_t *bd_addr,
 					bthh_protocol_mode_t protocolMode)
 {
+	struct hal_msg_cmd_bt_hid_set_protocol cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -136,7 +160,27 @@ static bt_status_t hh_set_protocol(bt_bdaddr_t *bd_addr,
 	if (!bd_addr)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	switch (protocolMode) {
+	case BTHH_REPORT_MODE:
+		cmd.mode = HAL_MSG_BT_HID_REPORT_PROTOCOL;
+		break;
+	case BTHH_BOOT_MODE:
+		cmd.mode = HAL_MSG_BT_HID_BOOT_PROTOCOL;
+		break;
+	case BTHH_UNSUPPORTED_MODE:
+		cmd.mode = HAL_MSG_BT_HID_UNSUPPORTED_PROTOCOL;
+		break;
+	}
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_SET_PROTOCOL,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0) {
+		error("Failed to set hid device protocol");
+		return BT_STATUS_FAIL;
+	}
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_get_report(bt_bdaddr_t *bd_addr,
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 1/5] android: Add initial code for hidhost virtual unplug
From: Ravi kumar Veeramally @ 2013-10-22 18:49 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally

This adds initial code for hidhost .virtual_unplug interface
---
 android/hal-hidhost.c |   10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/android/hal-hidhost.c b/android/hal-hidhost.c
index f941f2f..523ce6d 100644
--- a/android/hal-hidhost.c
+++ b/android/hal-hidhost.c
@@ -79,6 +79,8 @@ static bt_status_t hh_disconnect(bt_bdaddr_t *bd_addr)
 
 static bt_status_t hh_virtual_unplug(bt_bdaddr_t *bd_addr)
 {
+	struct hal_msg_cmd_bt_hid_vp cmd;
+
 	DBG("");
 
 	if (!interface_ready())
@@ -87,7 +89,13 @@ static bt_status_t hh_virtual_unplug(bt_bdaddr_t *bd_addr)
 	if (!bd_addr)
 		return BT_STATUS_PARM_INVALID;
 
-	return BT_STATUS_UNSUPPORTED;
+	memcpy(cmd.bdaddr, bd_addr, sizeof(cmd.bdaddr));
+
+	if (hal_ipc_cmd(HAL_SERVICE_ID_HIDHOST, HAL_MSG_OP_BT_HID_VP,
+			sizeof(cmd), &cmd, 0, NULL, NULL) < 0)
+		return BT_STATUS_FAIL;
+
+	return BT_STATUS_SUCCESS;
 }
 
 static bt_status_t hh_set_info(bt_bdaddr_t *bd_addr, bthh_hid_info_t hid_info)
-- 
1.7.9.5


^ permalink raw reply related

* [PATCH 6/6] adapter: Implement adapter enable and disable commands
From: Szymon Janc @ 2013-10-22 16:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1382460476-6604-1-git-send-email-szymon.janc@tieto.com>

This allows HAL to enable and disable adapter.
---
 android/adapter.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 63 insertions(+), 1 deletion(-)

diff --git a/android/adapter.c b/android/adapter.c
index 5621e67..1a3e3f4 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -290,12 +290,74 @@ bool bt_adapter_init(uint16_t index, struct mgmt *mgmt_if,
 	return adapter;
 }
 
+static void set_mode_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	if (status != MGMT_STATUS_SUCCESS) {
+		error("Failed to set mode: %s (0x%02x)",
+						mgmt_errstr(status), status);
+		return;
+	}
+
+	/*
+	 * The parameters are identical and also the task that is
+	 * required in both cases. So it is safe to just call the
+	 * event handling functions here.
+	 */
+	new_settings_callback(default_adapter->index, length, param,
+							default_adapter);
+}
+
+static bool set_mode(uint16_t opcode, uint8_t mode)
+{
+	struct mgmt_mode cp;
+
+	memset(&cp, 0, sizeof(cp));
+	cp.val = mode;
+
+	DBG("opcode=0x%x mode=0x%x", opcode, mode);
+
+	if (mgmt_send(default_adapter->mgmt, opcode, default_adapter->index,
+			sizeof(cp), &cp, set_mode_complete, NULL, NULL) > 0)
+		return true;
+
+	error("Failed to set mode");
+
+	return false;
+}
+
 void bt_adapter_handle_cmd(GIOChannel *io, uint8_t opcode, void *buf,
 								uint16_t len)
 {
+	uint8_t status = HAL_ERROR_FAILED;
+
 	switch (opcode) {
+	case HAL_MSG_OP_BT_ENABLE:
+		if (default_adapter->current_settings & MGMT_SETTING_POWERED) {
+			status = HAL_ERROR_DONE;
+			break;
+		}
+
+		if (set_mode(MGMT_OP_SET_POWERED, 0x01)) {
+			ipc_send(io, HAL_SERVICE_ID_CORE, opcode, 0, NULL, -1);
+			return;
+		}
+		break;
+	case HAL_MSG_OP_BT_DISABLE:
+		if (!(default_adapter->current_settings & MGMT_SETTING_POWERED)) {
+			status = HAL_ERROR_DONE;
+			break;
+		}
+
+		if (set_mode(MGMT_OP_SET_POWERED, 0x00)) {
+			ipc_send(io, HAL_SERVICE_ID_CORE, opcode, 0, NULL, -1);
+			return;
+		}
+		break;
 	default:
-		ipc_send_error(io, HAL_SERVICE_ID_BLUETOOTH, HAL_ERROR_FAILED);
+		DBG("Unhandled command, opcode 0x%x", opcode);
 		break;
 	}
+
+	ipc_send_error(io, HAL_SERVICE_ID_BLUETOOTH, status);
 }
-- 
1.8.4


^ permalink raw reply related

* [PATCH 5/6] android: Fix error response for unknown command
From: Szymon Janc @ 2013-10-22 16:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1382460476-6604-1-git-send-email-szymon.janc@tieto.com>

Use "bluetooth" service id when replying to adapter commands.
---
 android/adapter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/android/adapter.c b/android/adapter.c
index f891bc2..5621e67 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -295,7 +295,7 @@ void bt_adapter_handle_cmd(GIOChannel *io, uint8_t opcode, void *buf,
 {
 	switch (opcode) {
 	default:
-		ipc_send_error(io, HAL_SERVICE_ID_CORE, HAL_ERROR_FAILED);
+		ipc_send_error(io, HAL_SERVICE_ID_BLUETOOTH, HAL_ERROR_FAILED);
 		break;
 	}
 }
-- 
1.8.4


^ permalink raw reply related

* [PATCH 4/6] android/hal: Use correct service id for adapter commands
From: Szymon Janc @ 2013-10-22 16:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1382460476-6604-1-git-send-email-szymon.janc@tieto.com>

This fix enabling and disabling adapter commands.
---
 android/hal-bluetooth.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/android/hal-bluetooth.c b/android/hal-bluetooth.c
index 852dc85..d988778 100644
--- a/android/hal-bluetooth.c
+++ b/android/hal-bluetooth.c
@@ -104,8 +104,8 @@ static int enable(void)
 	if (!interface_ready())
 		return BT_STATUS_NOT_READY;
 
-	return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_MSG_OP_BT_ENABLE, 0, NULL,
-								0, NULL, NULL);
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_MSG_OP_BT_ENABLE, 0,
+							NULL, 0, NULL, NULL);
 }
 
 static int disable(void)
@@ -115,8 +115,8 @@ static int disable(void)
 	if (!interface_ready())
 		return BT_STATUS_NOT_READY;
 
-	return hal_ipc_cmd(HAL_SERVICE_ID_CORE, HAL_MSG_OP_BT_DISABLE, 0, NULL,
-								0, NULL, NULL);
+	return hal_ipc_cmd(HAL_SERVICE_ID_BLUETOOTH, HAL_MSG_OP_BT_DISABLE, 0,
+							NULL, 0, NULL, NULL);
 }
 
 static void cleanup(void)
-- 
1.8.4


^ permalink raw reply related

* [PATCH 3/6] adapter: Print debug message with IPC message header parameters
From: Szymon Janc @ 2013-10-22 16:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1382460476-6604-1-git-send-email-szymon.janc@tieto.com>

This is usefull while debugging HAL IPC.
---
 android/main.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/android/main.c b/android/main.c
index f3e2baa..988d08b 100644
--- a/android/main.c
+++ b/android/main.c
@@ -151,6 +151,9 @@ static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
 		goto fail;
 	}
 
+	DBG("service_id %u opcode %u len %u", msg->service_id, msg->opcode,
+								msg->len);
+
 	switch (msg->service_id) {
 	case HAL_SERVICE_ID_CORE:
 		handle_service_core(msg->opcode, buf + sizeof(*msg), msg->len);
-- 
1.8.4


^ permalink raw reply related

* [PATCH 2/6] android: Add initial code for handling adapter commands
From: Szymon Janc @ 2013-10-22 16:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1382460476-6604-1-git-send-email-szymon.janc@tieto.com>

No commands are handled yet.
---
 android/adapter.c | 14 ++++++++++++++
 android/adapter.h |  3 +++
 android/main.c    |  4 ++++
 3 files changed, 21 insertions(+)

diff --git a/android/adapter.c b/android/adapter.c
index cae3187..f891bc2 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -23,10 +23,14 @@
 
 #include <errno.h>
 
+#include <glib.h>
+
 #include "lib/bluetooth.h"
 #include "src/shared/mgmt.h"
 #include "lib/mgmt.h"
 #include "log.h"
+#include "hal-msg.h"
+#include "ipc.h"
 #include "adapter.h"
 
 struct bt_adapter {
@@ -285,3 +289,13 @@ bool bt_adapter_init(uint16_t index, struct mgmt *mgmt_if,
 
 	return adapter;
 }
+
+void bt_adapter_handle_cmd(GIOChannel *io, uint8_t opcode, void *buf,
+								uint16_t len)
+{
+	switch (opcode) {
+	default:
+		ipc_send_error(io, HAL_SERVICE_ID_CORE, HAL_ERROR_FAILED);
+		break;
+	}
+}
diff --git a/android/adapter.h b/android/adapter.h
index d8ad191..bfd4932 100644
--- a/android/adapter.h
+++ b/android/adapter.h
@@ -34,3 +34,6 @@ typedef void (*bt_adapter_ready)(struct bt_adapter *adapter, int err);
 
 bool bt_adapter_init(uint16_t index, struct mgmt *mgmt_if,
 						bt_adapter_ready func);
+
+void bt_adapter_handle_cmd(GIOChannel *io, uint8_t opcode, void *buf,
+								uint16_t len);
diff --git a/android/main.c b/android/main.c
index d4916ca..f3e2baa 100644
--- a/android/main.c
+++ b/android/main.c
@@ -155,6 +155,10 @@ static gboolean cmd_watch_cb(GIOChannel *io, GIOCondition cond,
 	case HAL_SERVICE_ID_CORE:
 		handle_service_core(msg->opcode, buf + sizeof(*msg), msg->len);
 		break;
+	case HAL_SERVICE_ID_BLUETOOTH:
+		bt_adapter_handle_cmd(hal_cmd_io, msg->opcode, msg->payload,
+								msg->len);
+		break;
 	default:
 		ipc_send_error(hal_cmd_io, msg->service_id, HAL_ERROR_FAILED);
 		break;
-- 
1.8.4


^ permalink raw reply related

* [PATCH 1/6] android: Handle mgmt changed events
From: Szymon Janc @ 2013-10-22 16:47 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Andrei Emeltchenko

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

Add code handling changing adapter settings.
---
 android/adapter.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)

diff --git a/android/adapter.c b/android/adapter.c
index 4009f4d..cae3187 100644
--- a/android/adapter.c
+++ b/android/adapter.c
@@ -69,6 +69,134 @@ failed:
 	adapter->ready(NULL, err);
 }
 
+static void mgmt_local_name_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct bt_adapter *adapter = user_data;
+	const struct mgmt_cp_set_local_name *rp = param;
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of local name changed parameters");
+		return;
+	}
+
+	if (!g_strcmp0(adapter->name, (const char *) rp->name))
+		return;
+
+	DBG("name: %s", rp->name);
+
+	g_free(adapter->name);
+	adapter->name = g_strdup((const char *) rp->name);
+
+	/* TODO Update services if needed */
+}
+
+static void settings_changed_connectable(struct bt_adapter *adapter)
+{
+	/* TODO */
+}
+
+static void settings_changed_discoverable(struct bt_adapter *adapter)
+{
+	/* TODO */
+}
+
+static void settings_changed(struct bt_adapter *adapter, uint32_t settings)
+{
+	uint32_t changed_mask;
+
+	changed_mask = adapter->current_settings ^ settings;
+
+	adapter->current_settings = settings;
+
+	DBG("0x%08x", changed_mask);
+
+	if (changed_mask & MGMT_SETTING_POWERED) {
+		info("Powered");
+
+		/*
+		if (adapter->current_settings & MGMT_SETTING_POWERED)
+			start_adapter()
+		else
+			stop_adapter()
+		*/
+	}
+
+	if (changed_mask & MGMT_SETTING_CONNECTABLE) {
+		DBG("Connectable");
+
+		settings_changed_connectable(adapter);
+	}
+
+	if (changed_mask & MGMT_SETTING_DISCOVERABLE) {
+		DBG("Discoverable");
+
+		settings_changed_discoverable(adapter);
+	}
+}
+
+static void new_settings_callback(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct bt_adapter *adapter = user_data;
+	uint32_t settings;
+
+	if (length < sizeof(settings)) {
+		error("Wrong size of new settings parameters");
+		return;
+	}
+
+	settings = bt_get_le32(param);
+
+	DBG("settings: 0x%8.8x -> 0x%8.8x", adapter->current_settings,
+								settings);
+
+	if (settings == adapter->current_settings)
+		return;
+
+	settings_changed(adapter, settings);
+}
+
+static void mgmt_dev_class_changed_event(uint16_t index, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct bt_adapter *adapter = user_data;
+	const struct mgmt_cod *rp = param;
+	uint32_t dev_class;
+
+	if (length < sizeof(*rp)) {
+		error("Wrong size of class of device changed parameters");
+		return;
+	}
+
+	dev_class = rp->val[0] | (rp->val[1] << 8) | (rp->val[2] << 16);
+
+	if (dev_class == adapter->dev_class)
+		return;
+
+	DBG("Class: 0x%06x", dev_class);
+
+	adapter->dev_class = dev_class;
+
+	/* TODO: Inform prop change: Class */
+
+	/* TODO: Gatt attrib set*/
+}
+
+static void register_mgmt_handlers(struct bt_adapter *adapter)
+{
+	mgmt_register(adapter->mgmt, MGMT_EV_NEW_SETTINGS, 0,
+			new_settings_callback, adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED,
+			0, mgmt_dev_class_changed_event,
+			adapter, NULL);
+
+	mgmt_register(adapter->mgmt, MGMT_EV_LOCAL_NAME_CHANGED,
+			0, mgmt_local_name_changed_event,
+			adapter, NULL);
+}
+
 static void load_link_keys(struct bt_adapter *adapter, GSList *keys)
 {
 	struct mgmt_cp_load_link_keys *cp;
@@ -128,6 +256,7 @@ static void read_info_complete(uint8_t status, uint16_t length, const void *para
 	adapter->current_settings = btohs(rp->current_settings);
 
 	/* TODO: Register all event notification handlers */
+	register_mgmt_handlers(adapter);
 
 	load_link_keys(adapter, NULL);
 
-- 
1.8.4


^ permalink raw reply related

* [PATCH] android: Create header links for lib
From: Andrei Emeltchenko @ 2013-10-22 16:42 UTC (permalink / raw)
  To: linux-bluetooth

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

BlueZ headers are referring as bluetooth/* for headers which are
located in lib/*. Create symlinks for now until we find better solution.
---
 android/Android.mk |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/android/Android.mk b/android/Android.mk
index c4b0621..a13d6f1 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -41,6 +41,23 @@ LOCAL_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\"
 LOCAL_SHARED_LIBRARIES := \
 	libglib \
 
+lib_headers := \
+        bluetooth.h \
+        hci.h \
+        hci_lib.h \
+        sco.h \
+        l2cap.h \
+        sdp.h \
+        sdp_lib.h \
+        rfcomm.h \
+        bnep.h \
+        cmtp.h \
+        hidp.h \
+
+$(shell mkdir -p $(LOCAL_PATH)/../lib/bluetooth)
+
+$(foreach file,$(lib_headers), $(shell ln -sf ../$(file) $(LOCAL_PATH)/../lib/bluetooth/$(file)))
+
 LOCAL_MODULE := bluetoothd
 
 include $(BUILD_EXECUTABLE)
-- 
1.7.10.4


^ permalink raw reply related

* Re: [PATCH 1/5] android/hal: Unify HALs callbacks pointers definition
From: Johan Hedberg @ 2013-10-22 14:19 UTC (permalink / raw)
  To: Szymon Janc; +Cc: linux-bluetooth
In-Reply-To: <1382448299-16887-1-git-send-email-szymon.janc@tieto.com>

Hi Szymon,

On Tue, Oct 22, 2013, Szymon Janc wrote:
> Those are not suppose to be modified or used outside of each HAL.
> ---
>  android/hal-av.c        | 2 +-
>  android/hal-bluetooth.c | 2 +-
>  android/hal-hidhost.c   | 2 +-
>  android/hal-pan.c       | 2 +-
>  4 files changed, 4 insertions(+), 4 deletions(-)

All five patches have been applied. Thanks.

Johan

^ permalink raw reply

* [PATCH 5/5] android: Remove not needed include
From: Szymon Janc @ 2013-10-22 13:24 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Szymon Janc
In-Reply-To: <1382448299-16887-1-git-send-email-szymon.janc@tieto.com>

---
 android/main.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/android/main.c b/android/main.c
index ba153a5..d4916ca 100644
--- a/android/main.c
+++ b/android/main.c
@@ -39,10 +39,6 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
-#if defined(ANDROID)
-#include <private/android_filesystem_config.h>
-#endif
-
 #include <glib.h>
 
 #include "log.h"
-- 
1.8.4


^ permalink raw reply related


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