Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH 05/10] profiles/network: Move pan sdp record function bnep and make it global
From: Ravi kumar Veeramally @ 2013-12-11 10:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1386756825-934-1-git-send-email-ravikumar.veeramally@linux.intel.com>

Moving pan sdp record function bnep, it is required in android/pan.
Even though it is not exactly related to bnep, but bnep.h|c is dbus
free files and shared with android/*.
---
 profiles/network/bnep.c   | 129 ++++++++++++++++++++++++++++++++++++++++++++++
 profiles/network/bnep.h   |   2 +
 profiles/network/server.c | 128 +--------------------------------------------
 3 files changed, 132 insertions(+), 127 deletions(-)

diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 08037e6..bbccd87 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -39,6 +39,8 @@
 #include <bluetooth/bluetooth.h>
 #include <bluetooth/l2cap.h>
 #include <bluetooth/bnep.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
 
 #include <glib.h>
 
@@ -542,3 +544,130 @@ uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
 
 	return BNEP_SUCCESS;
 }
+
+sdp_record_t *pan_record(const char *name, uint16_t id, gboolean security)
+{
+	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+	uuid_t root_uuid, pan, l2cap, bnep;
+	sdp_profile_desc_t profile[1];
+	sdp_list_t *proto[2];
+	sdp_data_t *v, *p;
+	uint16_t psm = BNEP_PSM, version = 0x0100;
+	uint16_t security_desc = (security ? 0x0001 : 0x0000);
+	uint16_t net_access_type = 0xfffe;
+	uint32_t max_net_access_rate = 0;
+	const char *desc = "Network service";
+	sdp_record_t *record;
+
+	record = sdp_record_alloc();
+	if (!record)
+		return NULL;
+
+	record->attrlist = NULL;
+	record->pattern = NULL;
+
+	switch (id) {
+	case BNEP_SVC_NAP:
+		sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
+		svclass = sdp_list_append(NULL, &pan);
+		sdp_set_service_classes(record, svclass);
+
+		sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
+		profile[0].version = 0x0100;
+		pfseq = sdp_list_append(NULL, &profile[0]);
+		sdp_set_profile_descs(record, pfseq);
+
+		sdp_set_info_attr(record, name, NULL, desc);
+
+		sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
+					SDP_UINT16, &net_access_type);
+		sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+					SDP_UINT32, &max_net_access_rate);
+		break;
+	case BNEP_SVC_GN:
+		sdp_uuid16_create(&pan, GN_SVCLASS_ID);
+		svclass = sdp_list_append(NULL, &pan);
+		sdp_set_service_classes(record, svclass);
+
+		sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
+		profile[0].version = 0x0100;
+		pfseq = sdp_list_append(NULL, &profile[0]);
+		sdp_set_profile_descs(record, pfseq);
+
+		sdp_set_info_attr(record, name, NULL, desc);
+		break;
+	case BNEP_SVC_PANU:
+		sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
+		svclass = sdp_list_append(NULL, &pan);
+		sdp_set_service_classes(record, svclass);
+
+		sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+		profile[0].version = 0x0100;
+		pfseq = sdp_list_append(NULL, &profile[0]);
+		sdp_set_profile_descs(record, pfseq);
+
+		sdp_set_info_attr(record, name, NULL, desc);
+		break;
+	default:
+		sdp_record_free(record);
+		return NULL;
+	}
+
+	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+	root = sdp_list_append(NULL, &root_uuid);
+	sdp_set_browse_groups(record, root);
+
+	sdp_uuid16_create(&l2cap, L2CAP_UUID);
+	proto[0] = sdp_list_append(NULL, &l2cap);
+	p = sdp_data_alloc(SDP_UINT16, &psm);
+	proto[0] = sdp_list_append(proto[0], p);
+	apseq    = sdp_list_append(NULL, proto[0]);
+
+	sdp_uuid16_create(&bnep, BNEP_UUID);
+	proto[1] = sdp_list_append(NULL, &bnep);
+	v = sdp_data_alloc(SDP_UINT16, &version);
+	proto[1] = sdp_list_append(proto[1], v);
+
+	/* Supported protocols */
+	{
+		uint16_t ptype[] = {
+			0x0800,  /* IPv4 */
+			0x0806,  /* ARP */
+		};
+		sdp_data_t *head, *pseq;
+		int p;
+
+		for (p = 0, head = NULL; p < 2; p++) {
+			sdp_data_t *data = sdp_data_alloc(SDP_UINT16,
+								&ptype[p]);
+			if (head)
+				sdp_seq_append(head, data);
+			else
+				head = data;
+		}
+		pseq = sdp_data_alloc(SDP_SEQ16, head);
+		proto[1] = sdp_list_append(proto[1], pseq);
+	}
+
+	apseq = sdp_list_append(apseq, proto[1]);
+
+	aproto = sdp_list_append(NULL, apseq);
+	sdp_set_access_protos(record, aproto);
+
+	sdp_add_lang_attr(record);
+
+	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
+				SDP_UINT16, &security_desc);
+
+	sdp_data_free(p);
+	sdp_data_free(v);
+	sdp_list_free(apseq, NULL);
+	sdp_list_free(root, NULL);
+	sdp_list_free(aproto, NULL);
+	sdp_list_free(proto[0], NULL);
+	sdp_list_free(proto[1], NULL);
+	sdp_list_free(svclass, NULL);
+	sdp_list_free(pfseq, NULL);
+
+	return record;
+}
diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h
index dd22c40..4f1f812 100644
--- a/profiles/network/bnep.h
+++ b/profiles/network/bnep.h
@@ -44,3 +44,5 @@ ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
 uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role);
 uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
 								uint16_t *src);
+
+sdp_record_t *pan_record(const char *name, uint16_t id, gboolean security);
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 73741ec..95564c6 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -125,132 +125,6 @@ static struct network_server *find_server_by_uuid(GSList *list,
 	return NULL;
 }
 
-static sdp_record_t *server_record_new(const char *name, uint16_t id)
-{
-	sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
-	uuid_t root_uuid, pan, l2cap, bnep;
-	sdp_profile_desc_t profile[1];
-	sdp_list_t *proto[2];
-	sdp_data_t *v, *p;
-	uint16_t psm = BNEP_PSM, version = 0x0100;
-	uint16_t security_desc = (security ? 0x0001 : 0x0000);
-	uint16_t net_access_type = 0xfffe;
-	uint32_t max_net_access_rate = 0;
-	const char *desc = "Network service";
-	sdp_record_t *record;
-
-	record = sdp_record_alloc();
-	if (!record)
-		return NULL;
-
-	record->attrlist = NULL;
-	record->pattern = NULL;
-
-	switch (id) {
-	case BNEP_SVC_NAP:
-		sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
-		svclass = sdp_list_append(NULL, &pan);
-		sdp_set_service_classes(record, svclass);
-
-		sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
-		profile[0].version = 0x0100;
-		pfseq = sdp_list_append(NULL, &profile[0]);
-		sdp_set_profile_descs(record, pfseq);
-
-		sdp_set_info_attr(record, name, NULL, desc);
-
-		sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE,
-					SDP_UINT16, &net_access_type);
-		sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
-					SDP_UINT32, &max_net_access_rate);
-		break;
-	case BNEP_SVC_GN:
-		sdp_uuid16_create(&pan, GN_SVCLASS_ID);
-		svclass = sdp_list_append(NULL, &pan);
-		sdp_set_service_classes(record, svclass);
-
-		sdp_uuid16_create(&profile[0].uuid, GN_PROFILE_ID);
-		profile[0].version = 0x0100;
-		pfseq = sdp_list_append(NULL, &profile[0]);
-		sdp_set_profile_descs(record, pfseq);
-
-		sdp_set_info_attr(record, name, NULL, desc);
-		break;
-	case BNEP_SVC_PANU:
-		sdp_uuid16_create(&pan, PANU_SVCLASS_ID);
-		svclass = sdp_list_append(NULL, &pan);
-		sdp_set_service_classes(record, svclass);
-
-		sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
-		profile[0].version = 0x0100;
-		pfseq = sdp_list_append(NULL, &profile[0]);
-		sdp_set_profile_descs(record, pfseq);
-
-		sdp_set_info_attr(record, name, NULL, desc);
-		break;
-	default:
-		sdp_record_free(record);
-		return NULL;
-	}
-
-	sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
-	root = sdp_list_append(NULL, &root_uuid);
-	sdp_set_browse_groups(record, root);
-
-	sdp_uuid16_create(&l2cap, L2CAP_UUID);
-	proto[0] = sdp_list_append(NULL, &l2cap);
-	p = sdp_data_alloc(SDP_UINT16, &psm);
-	proto[0] = sdp_list_append(proto[0], p);
-	apseq    = sdp_list_append(NULL, proto[0]);
-
-	sdp_uuid16_create(&bnep, BNEP_UUID);
-	proto[1] = sdp_list_append(NULL, &bnep);
-	v = sdp_data_alloc(SDP_UINT16, &version);
-	proto[1] = sdp_list_append(proto[1], v);
-
-	/* Supported protocols */
-	{
-		uint16_t ptype[] = {
-			0x0800,  /* IPv4 */
-			0x0806,  /* ARP */
-		};
-		sdp_data_t *head, *pseq;
-		int p;
-
-		for (p = 0, head = NULL; p < 2; p++) {
-			sdp_data_t *data = sdp_data_alloc(SDP_UINT16, &ptype[p]);
-			if (head)
-				sdp_seq_append(head, data);
-			else
-				head = data;
-		}
-		pseq = sdp_data_alloc(SDP_SEQ16, head);
-		proto[1] = sdp_list_append(proto[1], pseq);
-	}
-
-	apseq = sdp_list_append(apseq, proto[1]);
-
-	aproto = sdp_list_append(NULL, apseq);
-	sdp_set_access_protos(record, aproto);
-
-	sdp_add_lang_attr(record);
-
-	sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC,
-				SDP_UINT16, &security_desc);
-
-	sdp_data_free(p);
-	sdp_data_free(v);
-	sdp_list_free(apseq, NULL);
-	sdp_list_free(root, NULL);
-	sdp_list_free(aproto, NULL);
-	sdp_list_free(proto[0], NULL);
-	sdp_list_free(proto[1], NULL);
-	sdp_list_free(svclass, NULL);
-	sdp_list_free(pfseq, NULL);
-
-	return record;
-}
-
 static int server_connadd(struct network_server *ns,
 				struct network_session *session,
 				uint16_t dst_role)
@@ -497,7 +371,7 @@ static uint32_t register_server_record(struct network_server *ns)
 {
 	sdp_record_t *record;
 
-	record = server_record_new(ns->name, ns->id);
+	record = pan_record(ns->name, ns->id, security);
 	if (!record) {
 		error("Unable to allocate new service record");
 		return 0;
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 04/10] profiles/network/server: Delete function which does nothing
From: Ravi kumar Veeramally @ 2013-12-11 10:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1386756825-934-1-git-send-email-ravikumar.veeramally@linux.intel.com>

---
 profiles/network/manager.c | 2 --
 profiles/network/server.c  | 4 ----
 profiles/network/server.h  | 1 -
 3 files changed, 7 deletions(-)

diff --git a/profiles/network/manager.c b/profiles/network/manager.c
index 8ac2dec..47aebcb 100644
--- a/profiles/network/manager.c
+++ b/profiles/network/manager.c
@@ -200,8 +200,6 @@ static int network_init(void)
 
 static void network_exit(void)
 {
-	server_exit();
-
 	btd_profile_unregister(&panu_profile);
 	btd_profile_unregister(&gn_profile);
 	btd_profile_unregister(&nap_profile);
diff --git a/profiles/network/server.c b/profiles/network/server.c
index cf34932..73741ec 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -493,10 +493,6 @@ int server_init(gboolean secure)
 	return 0;
 }
 
-void server_exit(void)
-{
-}
-
 static uint32_t register_server_record(struct network_server *ns)
 {
 	sdp_record_t *record;
diff --git a/profiles/network/server.h b/profiles/network/server.h
index 2edd342..a76e6f7 100644
--- a/profiles/network/server.h
+++ b/profiles/network/server.h
@@ -22,6 +22,5 @@
  */
 
 int server_init(gboolean secure);
-void server_exit(void);
 int server_register(struct btd_adapter *adapter, uint16_t id);
 int server_unregister(struct btd_adapter *adapter, uint16_t id);
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 03/10] bnep: Move bnep related calls to bnep.h|c
From: Ravi kumar Veeramally @ 2013-12-11 10:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1386756825-934-1-git-send-email-ravikumar.veeramally@linux.intel.com>

Moving bnep related calls to bnep.h|c to reduce redundancy
while using same in android/*.
---
 profiles/network/bnep.c   | 67 +++++++++++++++++++++++++++++++++++++++++++++++
 profiles/network/bnep.h   |  5 +++-
 profiles/network/server.c | 66 ----------------------------------------------
 3 files changed, 71 insertions(+), 67 deletions(-)

diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 57dfbf9..08037e6 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -475,3 +475,70 @@ ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp)
 
 	return send(sk, &rsp, sizeof(rsp), 0);
 }
+
+uint16_t bnep_setup_chk(uint16_t dst, uint16_t src)
+{
+	/* Allowed PAN Profile scenarios */
+	switch (dst) {
+	case BNEP_SVC_NAP:
+	case BNEP_SVC_GN:
+		if (src == BNEP_SVC_PANU)
+			return 0;
+		return BNEP_CONN_INVALID_SRC;
+	case BNEP_SVC_PANU:
+		if (src == BNEP_SVC_PANU ||  src == BNEP_SVC_GN ||
+							src == BNEP_SVC_NAP)
+			return 0;
+
+		return BNEP_CONN_INVALID_SRC;
+	}
+
+	return BNEP_CONN_INVALID_DST;
+}
+
+uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
+								uint16_t *src)
+{
+	const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+					0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+	uint8_t *dest, *source;
+	uint32_t val;
+
+	dest = req->service;
+	source = req->service + req->uuid_size;
+
+	switch (req->uuid_size) {
+	case 2: /* UUID16 */
+		*dst = bt_get_be16(dest);
+		*src = bt_get_be16(source);
+		break;
+	case 16: /* UUID128 */
+		/* Check that the bytes in the UUID, except the service ID
+		 * itself, are correct. The service ID is checked in
+		 * bnep_setup_chk(). */
+		if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0)
+			return BNEP_CONN_INVALID_DST;
+		if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
+			return BNEP_CONN_INVALID_SRC;
+
+		/* Intentional no-break */
+
+	case 4: /* UUID32 */
+		val = bt_get_be32(dest);
+		if (val > 0xffff)
+			return BNEP_CONN_INVALID_DST;
+
+		*dst = val;
+
+		val = bt_get_be32(source);
+		if (val > 0xffff)
+			return BNEP_CONN_INVALID_SRC;
+
+		*src = val;
+		break;
+	default:
+		return BNEP_CONN_INVALID_SVC;
+	}
+
+	return BNEP_SUCCESS;
+}
diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h
index dea0319..dd22c40 100644
--- a/profiles/network/bnep.h
+++ b/profiles/network/bnep.h
@@ -40,4 +40,7 @@ typedef void (*bnep_connect_cb) (GIOChannel *chan, char *iface, int err,
 int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb,
 								void *data);
 
-ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
\ No newline at end of file
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
+uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role);
+uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
+								uint16_t *src);
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 296ddd8..cf34932 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -280,72 +280,6 @@ static int server_connadd(struct network_server *ns,
 	return 0;
 }
 
-static uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role)
-{
-	/* Allowed PAN Profile scenarios */
-	switch (dst_role) {
-	case BNEP_SVC_NAP:
-	case BNEP_SVC_GN:
-		if (src_role == BNEP_SVC_PANU)
-			return 0;
-		return BNEP_CONN_INVALID_SRC;
-	case BNEP_SVC_PANU:
-		if (src_role == BNEP_SVC_PANU ||
-				src_role == BNEP_SVC_GN ||
-				src_role == BNEP_SVC_NAP)
-			return 0;
-
-		return BNEP_CONN_INVALID_SRC;
-	}
-
-	return BNEP_CONN_INVALID_DST;
-}
-
-static uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req,
-				uint16_t *dst_role, uint16_t *src_role)
-{
-	const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
-				0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
-	uint8_t *dest, *source;
-	uint32_t val;
-
-	dest = req->service;
-	source = req->service + req->uuid_size;
-
-	switch (req->uuid_size) {
-	case 2: /* UUID16 */
-		*dst_role = bt_get_be16(dest);
-		*src_role = bt_get_be16(source);
-		break;
-	case 16: /* UUID128 */
-		/* Check that the bytes in the UUID, except the service ID
-		 * itself, are correct. The service ID is checked in
-		 * bnep_setup_chk(). */
-		if (memcmp(&dest[4], bt_base, sizeof(bt_base)) != 0)
-			return BNEP_CONN_INVALID_DST;
-		if (memcmp(&source[4], bt_base, sizeof(bt_base)) != 0)
-			return BNEP_CONN_INVALID_SRC;
-
-		/* Intentional no-break */
-
-	case 4: /* UUID32 */
-		val = bt_get_be32(dest);
-		if (val > 0xffff)
-			return BNEP_CONN_INVALID_DST;
-		*dst_role = val;
-
-		val = bt_get_be32(source);
-		if (val > 0xffff)
-			return BNEP_CONN_INVALID_SRC;
-		*src_role = val;
-		break;
-	default:
-		return BNEP_CONN_INVALID_SVC;
-	}
-
-	return BNEP_SUCCESS;
-}
-
 static void session_free(void *data)
 {
 	struct network_session *session = data;
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 02/10] bnep: Rename send ctrl_rsp and make it global
From: Ravi kumar Veeramally @ 2013-12-11 10:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1386756825-934-1-git-send-email-ravikumar.veeramally@linux.intel.com>

Renaming send_bnep_ctrl_rsp to bnep_send_ctrl_rsp and moving
to bnep.h. It is required in android/*.
---
 profiles/network/bnep.c   | 11 +++++++++++
 profiles/network/bnep.h   |  2 ++
 profiles/network/server.c | 13 +------------
 3 files changed, 14 insertions(+), 12 deletions(-)

diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index aa980a2..57dfbf9 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -464,3 +464,14 @@ int bnep_del_from_bridge(const char *devname, const char *bridge)
 
 	return 0;
 }
+
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp)
+{
+	struct bnep_control_rsp rsp;
+
+	rsp.type = type;
+	rsp.ctrl = ctrl;
+	rsp.resp = htons(resp);
+
+	return send(sk, &rsp, sizeof(rsp), 0);
+}
diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h
index 1905a98..dea0319 100644
--- a/profiles/network/bnep.h
+++ b/profiles/network/bnep.h
@@ -39,3 +39,5 @@ typedef void (*bnep_connect_cb) (GIOChannel *chan, char *iface, int err,
 								void *data);
 int bnep_connect(int sk, uint16_t src, uint16_t dst, bnep_connect_cb conn_cb,
 								void *data);
+
+ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
\ No newline at end of file
diff --git a/profiles/network/server.c b/profiles/network/server.c
index c777cc1..296ddd8 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -251,17 +251,6 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id)
 	return record;
 }
 
-static ssize_t send_bnep_ctrl_rsp(int sk, uint16_t val)
-{
-	struct bnep_control_rsp rsp;
-
-	rsp.type = BNEP_CONTROL;
-	rsp.ctrl = BNEP_SETUP_CONN_RSP;
-	rsp.resp = htons(val);
-
-	return send(sk, &rsp, sizeof(rsp), 0);
-}
-
 static int server_connadd(struct network_server *ns,
 				struct network_session *session,
 				uint16_t dst_role)
@@ -462,7 +451,7 @@ static gboolean bnep_setup(GIOChannel *chan,
 	rsp = BNEP_SUCCESS;
 
 reply:
-	send_bnep_ctrl_rsp(sk, rsp);
+	bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
 
 	return FALSE;
 }
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 01/10] bnep: Rename bnep_kill_connection to bnep_conndel
From: Ravi kumar Veeramally @ 2013-12-11 10:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally
In-Reply-To: <1386756825-934-1-git-send-email-ravikumar.veeramally@linux.intel.com>

Renaming bnep_kill_connection to bnep_conndel to maintain
consistency with bnep_connadd function name.
---
 android/pan.c                 | 2 +-
 profiles/network/bnep.c       | 2 +-
 profiles/network/bnep.h       | 3 +--
 profiles/network/connection.c | 2 +-
 profiles/network/server.c     | 2 +-
 5 files changed, 5 insertions(+), 6 deletions(-)

diff --git a/android/pan.c b/android/pan.c
index 6b098b2..f74f1a7 100644
--- a/android/pan.c
+++ b/android/pan.c
@@ -288,7 +288,7 @@ static void bt_pan_disconnect(const void *buf, uint16_t len)
 	}
 
 	bnep_if_down(dev->iface);
-	bnep_kill_connection(&dst);
+	bnep_conndel(&dst);
 
 	bt_pan_notify_conn_state(dev, HAL_PAN_STATE_DISCONNECTED);
 	pan_device_free(dev);
diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 912c0c2..aa980a2 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -160,7 +160,7 @@ int bnep_cleanup(void)
 	return 0;
 }
 
-int bnep_kill_connection(const bdaddr_t *dst)
+int bnep_conndel(const bdaddr_t *dst)
 {
 	struct bnep_conndel_req req;
 
diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h
index 9043e46..1905a98 100644
--- a/profiles/network/bnep.h
+++ b/profiles/network/bnep.h
@@ -28,9 +28,8 @@ uint16_t bnep_service_id(const char *svc);
 const char *bnep_uuid(uint16_t id);
 const char *bnep_name(uint16_t id);
 
-int bnep_kill_connection(const bdaddr_t *dst);
-
 int bnep_connadd(int sk, uint16_t role, char *dev);
+int bnep_conndel(const bdaddr_t *dst);
 int bnep_if_up(const char *devname);
 int bnep_if_down(const char *devname);
 int bnep_add_to_bridge(const char *devname, const char *bridge);
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index 9aff319..fb3e1ce 100644
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -171,7 +171,7 @@ static void connection_destroy(DBusConnection *conn, void *user_data)
 
 	if (nc->state == CONNECTED) {
 		bnep_if_down(nc->dev);
-		bnep_kill_connection(device_get_address(nc->peer->device));
+		bnep_conndel(device_get_address(nc->peer->device));
 	} else if (nc->io)
 		cancel_connection(nc, -EIO);
 }
diff --git a/profiles/network/server.c b/profiles/network/server.c
index b3aab11..c777cc1 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -608,7 +608,7 @@ static void server_remove_sessions(struct network_server *ns)
 		bnep_del_from_bridge(session->dev, ns->bridge);
 		bnep_if_down(session->dev);
 
-		bnep_kill_connection(&session->dst);
+		bnep_conndel(&session->dst);
 	}
 
 	g_slist_free_full(ns->sessions, session_free);
-- 
1.8.3.2


^ permalink raw reply related

* [PATCH 00/10] Refactoring bnep code to reduce redundancy
From: Ravi kumar Veeramally @ 2013-12-11 10:13 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Ravi kumar Veeramally

This patch set contains refactoring of bnep functionality, minor
fixes in android/pan and adding PAN profile NAP sdp record to
android.

Ravi kumar Veeramally (10):
  bnep: Rename bnep_kill_connection to bnep_conndel
  bnep: Rename send ctrl_rsp and make it global
  bnep: Move bnep related calls to bnep.h|c
  profiles/network/server: Delete function which does nothing
  profiles/network: Move pan sdp record function bnep and make it global
  android/pan: Remove channel unref which causing disconnection
  android/pan: Fix missing cleanup calls
  android/pan: Fix minor white space
  android/pan: Free connected pan devices on profile unregister call
  android/pan: Add PAN NAP sdp record fo server role

 android/pan.c                 |  40 +++++++-
 profiles/network/bnep.c       | 209 ++++++++++++++++++++++++++++++++++++++++-
 profiles/network/bnep.h       |  10 +-
 profiles/network/connection.c |   2 +-
 profiles/network/manager.c    |   2 -
 profiles/network/server.c     | 213 +-----------------------------------------
 profiles/network/server.h     |   1 -
 7 files changed, 256 insertions(+), 221 deletions(-)

-- 
1.8.3.2


^ permalink raw reply

* [PATCH] hciemu: Print error in case hci_vhci is not loaded
From: Andrei Emeltchenko @ 2013-12-11  9:36 UTC (permalink / raw)
  To: linux-bluetooth

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

Error message should indicate that module is not loaded:
Opening /dev/vhci failed: No such file or directory
---
 src/shared/hciemu.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/shared/hciemu.c b/src/shared/hciemu.c
index c2b4748..9f4bfaf 100644
--- a/src/shared/hciemu.c
+++ b/src/shared/hciemu.c
@@ -31,6 +31,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdbool.h>
+#include <errno.h>
 #include <sys/socket.h>
 
 #include <glib.h>
@@ -216,6 +217,7 @@ static bool create_vhci(struct hciemu *hciemu)
 
 	fd = open("/dev/vhci", O_RDWR | O_NONBLOCK | O_CLOEXEC);
 	if (fd < 0) {
+		perror("Opening /dev/vhci failed");
 		btdev_destroy(btdev);
 		return false;
 	}
-- 
1.8.3.2


^ permalink raw reply related

* Re: mouse not found with bluez5 but was fine with bluez4
From: Johan Hedberg @ 2013-12-11  9:21 UTC (permalink / raw)
  To: Brian J. Murrell; +Cc: linux-bluetooth
In-Reply-To: <1386680927.11483.6.camel@pc.interlinx.bc.ca>

Hi Brian,

On Tue, Dec 10, 2013, Brian J. Murrell wrote:
> So, in chasing an issue with mouse pairing in another thread I was asked
> to upgrade to Fedora 20 to see if my mouse pairing issue was
> reproducible on bluez5 given that bluez4 is pretty dead now.
> 
> But the same mouse that could be found and paired on bluez4 (Fedora 19)
> is not even being found during the search on bluez5 (Fedora 20) on the
> exact same hardware (same machine in fact).  Bluez5 did manage to see
> and pair a headset though, just not this mouse.
> 
> What information/tests can I provide to help figure this out?

For one thing, it'd be good to determine whether this is a GUI thing (I
assume you're using the GNOME UI?) or something in BlueZ. The way to do
that would be to use the bluetoothctl tool that's part of BlueZ 5. The
commands you'd give it would go something like:

	agent on
	power on
	scan on
	<wait for mouse to show up>
	scan off
	pair <mouse addr>	(optional if the mouse doesn't support pairing)
	connect <mouse addr>
	trust <mouse addr>

Johan

^ permalink raw reply

* [PATCH v9 5/5] Bluetooth: Manually enable or disable 6LoWPAN between devices
From: Jukka Rissanen @ 2013-12-11  7:38 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386747530-15830-1-git-send-email-jukka.rissanen@linux.intel.com>

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

Before connecting the devices do this

echo Y > /sys/kernel/debug/bluetooth/hci0/6lowpan

This enables 6LoWPAN support and creates the bt0 interface
automatically when devices are finally connected.

Rebooting or unloading the bluetooth kernel module will also clear the
settings from the kernel.

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

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 8b8b5f8..e5b43cc 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -636,6 +636,57 @@ static int conn_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
 			conn_max_interval_set, "%llu\n");
 
+static ssize_t lowpan_read(struct file *file, char __user *user_buf,
+			   size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
+			    size_t count, loff_t *position)
+{
+	struct hci_dev *hdev = fp->private_data;
+	bool new_value, old_value;
+	char buf[32];
+	size_t buf_size = min(count, (sizeof(buf)-1));
+	ssize_t ret;
+
+	BT_DBG("dev %s count %zd", hdev->dev_name, count);
+
+	if (copy_from_user(buf, user_buffer, buf_size))
+		return -EFAULT;
+
+	buf[buf_size] = '\0';
+
+	if (strtobool(buf, &new_value) < 0)
+		return -EINVAL;
+
+	ret = -ENOENT;
+
+	if (ret == 0 && old_value == new_value)
+		return count;
+
+	if (new_value == true)
+		set_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+	else
+		clear_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+
+	return count;
+}
+
+static const struct file_operations lowpan_debugfs_fops = {
+	.open		= simple_open,
+	.read		= lowpan_read,
+	.write		= lowpan_write,
+	.llseek		= default_llseek,
+};
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -1406,6 +1457,8 @@ static int __hci_init(struct hci_dev *hdev)
 				    hdev, &conn_min_interval_fops);
 		debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
 				    hdev, &conn_max_interval_fops);
+		debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
+				    &lowpan_debugfs_fops);
 	}
 
 	return 0;
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v9 4/5] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Jukka Rissanen @ 2013-12-11  7:38 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386747530-15830-1-git-send-email-jukka.rissanen@linux.intel.com>

This is initial version of
http://tools.ietf.org/html/draft-ietf-6lo-btle-00

By default the 6LoWPAN support is not activated and user
needs to tweak /sys/kernel/debug/bluetooth/hci0/6lowpan
file.

The kernel needs IPv6 support before 6LoWPAN is usable.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 include/net/bluetooth/hci.h      |   1 +
 include/net/bluetooth/hci_core.h |   1 +
 include/net/bluetooth/l2cap.h    |   1 +
 net/bluetooth/6lowpan.c          | 874 +++++++++++++++++++++++++++++++++++++++
 net/bluetooth/6lowpan.h          |  26 ++
 net/bluetooth/Makefile           |   6 +-
 net/bluetooth/hci_event.c        |   3 +
 net/bluetooth/l2cap_core.c       |  12 +
 8 files changed, 923 insertions(+), 1 deletion(-)
 create mode 100644 net/bluetooth/6lowpan.c
 create mode 100644 net/bluetooth/6lowpan.h

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index cc2da73..5dc3d90 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -131,6 +131,7 @@ enum {
 	HCI_PERIODIC_INQ,
 	HCI_FAST_CONNECTABLE,
 	HCI_BREDR_ENABLED,
+	HCI_6LOWPAN_ENABLED,
 };
 
 /* A mask for the flags that are supposed to remain when a reset happens
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index b796161..f2f0cf5 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -448,6 +448,7 @@ enum {
 	HCI_CONN_SSP_ENABLED,
 	HCI_CONN_POWER_SAVE,
 	HCI_CONN_REMOTE_OOB,
+	HCI_CONN_6LOWPAN,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index e149e99..dbc4a89 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -136,6 +136,7 @@ struct l2cap_conninfo {
 #define L2CAP_FC_L2CAP		0x02
 #define L2CAP_FC_CONNLESS	0x04
 #define L2CAP_FC_A2MP		0x08
+#define L2CAP_FC_6LOWPAN        0x3e /* reserved and temporary value */
 
 /* 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..e76630c
--- /dev/null
+++ b/net/bluetooth/6lowpan.c
@@ -0,0 +1,874 @@
+/*
+   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/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> /* to get the address type */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "../ieee802154/6lowpan.h" /* for the compression support */
+
+#define IFACE_NAME_TEMPLATE "bt%d"
+#define EUI64_ADDR_LEN 8
+
+struct skb_cb {
+	struct in6_addr addr;
+	struct l2cap_conn *conn;
+};
+#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
+
+/* 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 network (=hciX device).
+ * The list contains struct lowpan_dev elements.
+ */
+static LIST_HEAD(bt_6lowpan_devices);
+static DEFINE_RWLOCK(devices_lock);
+
+struct lowpan_dev {
+	struct net_device *dev;
+	struct work_struct delete_netdev;
+	struct list_head list;
+};
+
+struct lowpan_peer {
+	struct list_head list;
+	struct l2cap_conn *conn;
+
+	/* peer addresses in various formats */
+	unsigned char eui64_addr[EUI64_ADDR_LEN];
+	struct in6_addr peer_addr;
+};
+
+struct lowpan_info {
+	struct net_device *net;
+	struct list_head peers;
+	atomic_t peer_count; /* number of items in peers list */
+
+	struct delayed_work notify_peers;
+};
+
+static inline struct lowpan_info *lowpan_info(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+
+static inline void peer_add(struct lowpan_info *info, struct lowpan_peer *peer)
+{
+	list_add(&peer->list, &info->peers);
+	atomic_inc(&info->peer_count);
+}
+
+static inline bool peer_del(struct lowpan_info *info, struct lowpan_peer *peer)
+{
+	list_del(&peer->list);
+
+	if (atomic_dec_and_test(&info->peer_count)) {
+		BT_DBG("last peer");
+		return true;
+	}
+
+	return false;
+}
+
+static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_info *info,
+						 bdaddr_t *ba, __u8 type)
+{
+	struct lowpan_peer *peer, *tmp;
+
+	BT_DBG("peers %d addr %pMR type %d", atomic_read(&info->peer_count),
+					     ba, type);
+
+	list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+		BT_DBG("addr %pMR type %d",
+			&peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+
+		if (!bacmp(&peer->conn->hcon->dst, ba))
+			return peer;
+	}
+
+	return NULL;
+}
+
+static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_info *info,
+						   struct l2cap_conn *conn)
+{
+	struct lowpan_peer *peer, *tmp;
+
+	list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+		if (peer->conn == conn)
+			return peer;
+	}
+
+	return NULL;
+}
+
+static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn,
+				       struct lowpan_info **dev)
+{
+	struct lowpan_dev *entry, *tmp;
+	struct lowpan_peer *peer = NULL;
+	unsigned long flags;
+
+	read_lock_irqsave(&devices_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		struct lowpan_info *info = lowpan_info(entry->dev);
+
+		peer = peer_lookup_conn(info, conn);
+		if (peer) {
+			if (dev)
+				*dev = info;
+			break;
+		}
+	}
+
+	read_unlock_irqrestore(&devices_lock, flags);
+
+	return peer;
+}
+
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+				   unsigned char *buf, int len)
+{
+	if (msg)
+		pr_debug("%s():%s: ", caller, msg);
+
+	print_hex_dump_debug("", DUMP_PREFIX_NONE,
+			     16, 1, buf, len, false);
+}
+
+/* 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)
+{
+	if (msg)
+		pr_debug("%s():%s:\n", caller, msg);
+
+	print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET,
+			     16, 1, buf, len, false);
+}
+
+static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sk_buff *skb_cp;
+	int ret;
+
+	skb_cp = skb_copy(skb, GFP_ATOMIC);
+	if (!skb_cp)
+		return -ENOMEM;
+
+	ret = netif_rx(skb_cp);
+
+	BT_DBG("receive skb %d", ret);
+	if (ret < 0)
+		return NET_RX_DROP;
+
+	return ret;
+}
+
+static int process_data(struct sk_buff *skb, struct net_device *dev,
+			struct l2cap_conn *conn)
+{
+	const u8 *saddr, *daddr;
+	u8 iphc0, iphc1;
+	struct lowpan_info *info;
+	struct lowpan_peer *peer;
+	unsigned long flags;
+
+	info = lowpan_info(dev);
+
+	read_lock_irqsave(&devices_lock, flags);
+	peer = peer_lookup_conn(info, conn);
+	read_unlock_irqrestore(&devices_lock, flags);
+	if (!peer)
+		goto drop;
+
+	saddr = peer->eui64_addr;
+	daddr = info->net->dev_addr;
+
+	/* at least two bytes will be used for the encoding */
+	if (skb->len < 2)
+		goto drop;
+
+	if (lowpan_fetch_skb_u8(skb, &iphc0))
+		goto drop;
+
+	if (lowpan_fetch_skb_u8(skb, &iphc1))
+		goto drop;
+
+	return lowpan_process_data(skb, dev,
+				   saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+				   daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+				   iphc0, iphc1, give_skb_to_upper);
+
+drop:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
+		    struct l2cap_conn *conn)
+{
+	struct sk_buff *local_skb;
+
+	if (!netif_running(dev))
+		goto drop;
+
+	if (dev->type != ARPHRD_6LOWPAN)
+		goto drop;
+
+	/* 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 */
+			local_skb = skb_clone(skb, GFP_ATOMIC);
+			if (!local_skb)
+				goto drop;
+			if (process_data(local_skb, dev, conn)
+							!= 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;
+}
+
+/* Packet from BT LE device */
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct lowpan_info *info = NULL;
+	struct lowpan_peer *peer;
+	int err;
+
+	peer = lookup_peer(conn, &info);
+	if (!peer)
+		return -ENOENT;
+
+	if (!info->net)
+		return -ENOENT;
+
+	err = recv_pkt(skb, info->net, conn);
+	BT_DBG("recv pkt %d", err);
+
+	return err;
+}
+
+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);
+}
+
+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_ATOMIC);
+		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;
+
+	/* FIXME: This mtu check should be not needed and atm is only used for
+	 * testing purposes
+	 */
+	if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
+		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_ATOMIC);
+	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;
+
+	skb = create_pdu(conn, msg, len, priority, dev);
+	if (IS_ERR(skb))
+		return -EINVAL;
+
+	do_send(conn, skb);
+	return 0;
+}
+
+static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
+			    bdaddr_t *addr, u8 *addr_type)
+{
+	u8 *eui64;
+
+	eui64 = ip6_daddr->s6_addr + 8;
+
+	addr->b[0] = eui64[7];
+	addr->b[1] = eui64[6];
+	addr->b[2] = eui64[5];
+	addr->b[3] = eui64[2];
+	addr->b[4] = eui64[1];
+	addr->b[5] = eui64[0];
+
+	addr->b[5] ^= 2;
+
+	/* Set universal/local bit to 0 */
+	if (addr->b[5] & 1) {
+		addr->b[5] &= ~1;
+		*addr_type = BDADDR_LE_PUBLIC;
+	} else {
+		*addr_type = BDADDR_LE_RANDOM;
+	}
+}
+
+static int header_create(struct sk_buff *skb, struct net_device *dev,
+		         unsigned short type, const void *_daddr,
+		         const void *_saddr, unsigned int len)
+{
+	struct ipv6hdr *hdr;
+	struct lowpan_info *info;
+	struct lowpan_peer *peer;
+	bdaddr_t addr, *any = BDADDR_ANY;
+	u8 *saddr, *daddr = any->b;
+	u8 addr_type;
+
+	if (type != ETH_P_IPV6)
+		return -EINVAL;
+
+	hdr = ipv6_hdr(skb);
+
+	info = lowpan_info(dev);
+
+	if (ipv6_addr_is_multicast(&hdr->daddr)) {
+		memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+		       sizeof(struct in6_addr));
+		lowpan_cb(skb)->conn = NULL;
+	} else {
+		unsigned long flags;
+
+		/* Get destination BT device from skb.
+		 * If there is no such peer then discard the packet.
+		 */
+		get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+
+		BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+
+		read_lock_irqsave(&devices_lock, flags);
+		peer = peer_lookup_ba(info, &addr, addr_type);
+		read_unlock_irqrestore(&devices_lock, flags);
+
+		if (!peer) {
+			BT_DBG("no such peer %pMR found", &addr);
+			return -ENOENT;
+		}
+
+		daddr = peer->eui64_addr;
+
+		memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+		       sizeof(struct in6_addr));
+		lowpan_cb(skb)->conn = peer->conn;
+	}
+
+	saddr = info->net->dev_addr;
+
+	return lowpan_header_compress(skb, dev, type, daddr, saddr, len);
+}
+
+/* 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 int send_mcast_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sk_buff *local_skb;
+	struct lowpan_dev *entry, *tmp;
+	int err = 0;
+	unsigned long flags;
+
+	read_lock_irqsave(&devices_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		struct lowpan_peer *pentry, *ptmp;
+		struct lowpan_info *info;
+
+		if (entry->dev != dev)
+			continue;
+
+		info = lowpan_info(entry->dev);
+
+		list_for_each_entry_safe(pentry, ptmp, &info->peers, list) {
+			local_skb = skb_clone(skb, GFP_ATOMIC);
+
+			err = send_pkt(pentry->conn, dev->dev_addr,
+				       pentry->eui64_addr,
+				       local_skb, dev);
+
+			kfree_skb(local_skb);
+		}
+	}
+
+	read_unlock_irqrestore(&devices_lock, flags);
+
+	return err;
+}
+
+static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	int err = -ENOENT;
+	unsigned char *eui64_addr;
+	struct lowpan_info *info;
+	struct lowpan_peer *peer;
+	bdaddr_t addr;
+	u8 addr_type;
+
+	if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) {
+		/* We need to send the packet to every device
+		 * behind this interface.
+		 */
+		err = send_mcast_pkt(skb, dev);
+	} else {
+		unsigned long flags;
+
+		get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+		eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
+		info = lowpan_info(dev);
+
+		read_lock_irqsave(&devices_lock, flags);
+		peer = peer_lookup_ba(info, &addr, addr_type);
+		read_unlock_irqrestore(&devices_lock, flags);
+
+		BT_DBG("xmit from %s to %pMR (%pI6c), peer %p", dev->name,
+			&addr, &lowpan_cb(skb)->addr, peer);
+
+		if (peer && peer->conn)
+			err = send_pkt(peer->conn, dev->dev_addr, eui64_addr,
+					skb, dev);
+	}
+	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		= bt_xmit,
+};
+
+static struct header_ops header_ops = {
+	.create	= header_create,
+};
+
+static void netdev_setup(struct net_device *dev)
+{
+	dev->addr_len		= EUI64_ADDR_LEN;
+	dev->type		= ARPHRD_6LOWPAN;
+
+	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->header_ops		= &header_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;
+
+	/* Universal/local bit set, RFC 4291 */
+	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 err;
+
+	rtnl_lock();
+	err = dev_open(net);
+	if (err < 0)
+		BT_INFO("iface %s cannot be opened (%d)", net->name, err);
+	rtnl_unlock();
+}
+
+static void do_notify_peers(struct work_struct *work)
+{
+	struct lowpan_info *info = container_of(work, struct lowpan_info,
+						notify_peers.work);
+
+	netdev_notify_peers(info->net); /* send neighbour adv at startup */
+}
+
+static bool is_bt_6lowpan(struct hci_conn *hcon)
+{
+	if (hcon->type != LE_LINK)
+		return false;
+
+	return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
+}
+
+/* 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 lowpan_info *dev = NULL;
+	struct lowpan_peer *peer = NULL;
+	struct net_device *net;
+	struct lowpan_dev *entry;
+	int err = 0;
+	unsigned long flags;
+
+	if (!is_bt_6lowpan(conn->hcon))
+		return 0;
+
+	peer = lookup_peer(conn, &dev);
+	if (peer)
+		return -EEXIST;
+
+	if (dev && !peer)
+		goto add_peer;
+
+	net = alloc_netdev(sizeof(struct lowpan_info), IFACE_NAME_TEMPLATE,
+		           netdev_setup);
+	if (!net)
+		return -ENOMEM;
+
+	dev = netdev_priv(net);
+	dev->net = net;
+	INIT_LIST_HEAD(&dev->peers);
+
+	set_dev_addr(net, &conn->hcon->src, conn->hcon->src_type);
+
+	net->netdev_ops = &netdev_ops;
+	SET_NETDEV_DEV(net, &conn->hcon->dev);
+	SET_NETDEV_DEVTYPE(net, &bt_type);
+
+	err = register_netdev(net);
+	if (err < 0) {
+		BT_INFO("register_netdev failed %d", err);
+		free_netdev(net);
+		goto out;
+	}
+
+	BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
+		net->ifindex, &conn->hcon->dst, &conn->hcon->src);
+	set_bit(__LINK_STATE_PRESENT, &net->state);
+
+	entry = kzalloc(sizeof(struct lowpan_dev), GFP_ATOMIC);
+	if (!entry) {
+		unregister_netdev(net);
+		return -ENOMEM;
+	}
+
+	entry->dev = net;
+
+	write_lock_irqsave(&devices_lock, flags);
+	INIT_LIST_HEAD(&entry->list);
+	list_add(&entry->list, &bt_6lowpan_devices);
+	write_unlock_irqrestore(&devices_lock, flags);
+
+	ifup(net);
+
+add_peer:
+	peer = kzalloc(sizeof(struct lowpan_peer), GFP_ATOMIC);
+	if (!peer)
+		return -ENOMEM;
+
+	peer->conn = conn;
+	memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
+
+	/* RFC 2464 ch. 5 */
+	peer->peer_addr.s6_addr[0] = 0xFE;
+	peer->peer_addr.s6_addr[1] = 0x80;
+	set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
+		conn->hcon->dst_type);
+
+	memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
+	       EUI64_ADDR_LEN);
+	peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
+				   * is done according RFC2464
+				   */
+
+	raw_dump_inline(__func__, "peer IPv6 address",
+			(unsigned char *)&peer->peer_addr, 16);
+	raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
+
+	write_lock_irqsave(&devices_lock, flags);
+	INIT_LIST_HEAD(&peer->list);
+	peer_add(dev, peer);
+	write_unlock_irqrestore(&devices_lock, flags);
+
+	/* Notifying peers about us needs to be done without locks held */
+	INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
+	schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
+
+out:
+	return err;
+}
+
+static void delete_netdev(struct work_struct *work)
+{
+	struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
+						delete_netdev);
+
+	unregister_netdev(entry->dev);
+
+	/* The entry pointer is deleted in device_event() */
+}
+
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+	struct lowpan_dev *entry, *tmp;
+	struct lowpan_info *info = NULL;
+	struct lowpan_peer *peer;
+	int err = -ENOENT;
+	unsigned long flags;
+	bool last = false;
+
+	if (!is_bt_6lowpan(conn->hcon))
+		return 0;
+
+	write_lock_irqsave(&devices_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		info = lowpan_info(entry->dev);
+		peer = peer_lookup_conn(info, conn);
+		if (peer) {
+			last = peer_del(info, peer);
+			err = 0;
+			break;
+		}
+	}
+
+	if (!err && last && info && !atomic_read(&info->peer_count)) {
+		write_unlock_irqrestore(&devices_lock, flags);
+
+		cancel_delayed_work_sync(&info->notify_peers);
+
+		/* bt_6lowpan_del_conn() is called with hci dev lock held which
+		 * means that we must delete the netdevice in worker thread.
+		 */
+		INIT_WORK(&entry->delete_netdev, delete_netdev);
+		schedule_work(&entry->delete_netdev);
+	} else
+		write_unlock_irqrestore(&devices_lock, flags);
+
+	return err;
+}
+
+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;
+	unsigned long flags;
+
+	if (dev->type != ARPHRD_6LOWPAN)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+		write_lock_irqsave(&devices_lock, flags);
+		list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
+					list) {
+			if (entry->dev == dev) {
+				list_del(&entry->list);
+				kfree(entry);
+				break;
+			}
+		}
+		write_unlock_irqrestore(&devices_lock, flags);
+		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..cc6827e 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,6 +10,10 @@ 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
+
+ifeq ($(CONFIG_IEEE802154_6LOWPAN),)
+  bluetooth-y +=  ../ieee802154/6lowpan_iphc.o
+endif
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5fb3df6..5f81245 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 	conn->handle = __le16_to_cpu(ev->handle);
 	conn->state = BT_CONNECTED;
 
+	if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
+		set_bit(HCI_CONN_6LOWPAN, &conn->flags);
+
 	hci_conn_add_sysfs(conn);
 
 	hci_proto_connect_cfm(conn, ev->status);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ae0054c..e97248d 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;
 
@@ -1468,6 +1469,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
 	BT_DBG("");
 
+	bt_6lowpan_add_conn(conn);
+
 	/* Check if we have socket listening on cid */
 	pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
 					  &hcon->src, &hcon->dst);
@@ -7093,6 +7096,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;
@@ -7160,6 +7167,8 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
 	BT_DBG("hcon %p reason %d", hcon, reason);
 
+	bt_6lowpan_del_conn(hcon->l2cap_data);
+
 	l2cap_conn_del(hcon, bt_to_errno(reason));
 }
 
@@ -7441,11 +7450,14 @@ int __init l2cap_init(void)
 	debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
 			   &le_default_mps);
 
+	bt_6lowpan_init();
+
 	return 0;
 }
 
 void l2cap_exit(void)
 {
+	bt_6lowpan_cleanup();
 	debugfs_remove(l2cap_debugfs);
 	l2cap_cleanup_sockets();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v9 3/5] ipv6: Add checks for 6LOWPAN ARP type
From: Jukka Rissanen @ 2013-12-11  7:38 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386747530-15830-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
---
 net/ipv6/addrconf.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 12c97d8..d125fcd 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1816,6 +1816,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
 		return addrconf_ifid_sit(eui, dev);
 	case ARPHRD_IPGRE:
 		return addrconf_ifid_gre(eui, dev);
+	case ARPHRD_6LOWPAN:
 	case ARPHRD_IEEE802154:
 		return addrconf_ifid_eui64(eui, dev);
 	case ARPHRD_IEEE1394:
@@ -2658,7 +2659,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_6LOWPAN)) {
 		/* Alas, we support only Ethernet autoconfiguration. */
 		return;
 	}
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v9 2/5] net: if_arp: add ARPHRD_6LOWPAN type
From: Jukka Rissanen @ 2013-12-11  7:38 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386747530-15830-1-git-send-email-jukka.rissanen@linux.intel.com>

Used for IPv6 over LoWPAN networks. Example of this is
Bluetooth 6LoWPAN network.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
---
 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..4d024d7 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -94,6 +94,7 @@
 #define ARPHRD_CAIF	822		/* CAIF media type		*/
 #define ARPHRD_IP6GRE	823		/* GRE over IPv6		*/
 #define ARPHRD_NETLINK	824		/* Netlink header		*/
+#define ARPHRD_6LOWPAN	825		/* IPv6 over LoWPAN             */
 
 #define ARPHRD_VOID	  0xFFFF	/* Void type, nothing is known */
 #define ARPHRD_NONE	  0xFFFE	/* zero header length */
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v9 1/5] 6lowpan: Moving generic compression code into 6lowpan_iphc.c
From: Jukka Rissanen @ 2013-12-11  7:38 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386747530-15830-1-git-send-email-jukka.rissanen@linux.intel.com>

Because the IEEE 802154 and Bluetooth share the IP header compression
and uncompression code, the common code is moved to 6lowpan_iphc.c
file.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Acked-by: Alexander Aring <alex.aring@gmail.com>
---
 net/ieee802154/6lowpan.c      | 753 ++-------------------------------------
 net/ieee802154/6lowpan.h      |  32 ++
 net/ieee802154/6lowpan_iphc.c | 807 ++++++++++++++++++++++++++++++++++++++++++
 net/ieee802154/Makefile       |   2 +-
 4 files changed, 875 insertions(+), 719 deletions(-)
 create mode 100644 net/ieee802154/6lowpan_iphc.c

diff --git a/net/ieee802154/6lowpan.c b/net/ieee802154/6lowpan.c
index 459e200..53d0bd5 100644
--- a/net/ieee802154/6lowpan.c
+++ b/net/ieee802154/6lowpan.c
@@ -62,9 +62,6 @@
 
 #include "6lowpan.h"
 
-/* TTL uncompression values */
-static const u8 lowpan_ttl_values[] = {0, 1, 64, 255};
-
 static LIST_HEAD(lowpan_devices);
 
 /* private device info */
@@ -135,347 +132,14 @@ static inline void lowpan_raw_dump_table(const char *caller, char *msg,
 #endif /* DEBUG */
 }
 
-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);
-}
-
-/*
- * Uncompress address function for source and
- * destination address(non-multicast).
- *
- * address_mode is sam value or dam value.
- */
-static int
-lowpan_uncompress_addr(struct sk_buff *skb,
-		struct in6_addr *ipaddr,
-		const u8 address_mode,
-		const struct ieee802154_addr *lladdr)
-{
-	bool fail;
-
-	switch (address_mode) {
-	case LOWPAN_IPHC_ADDR_00:
-		/* for global link addresses */
-		fail = lowpan_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 = lowpan_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 = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
-		break;
-	case LOWPAN_IPHC_ADDR_03:
-		fail = false;
-		switch (lladdr->addr_type) {
-		case IEEE802154_ADDR_LONG:
-			/* fe:80::XXXX:XXXX:XXXX:XXXX
-			 *        \_________________/
-			 *              hwaddr
-			 */
-			ipaddr->s6_addr[0] = 0xFE;
-			ipaddr->s6_addr[1] = 0x80;
-			memcpy(&ipaddr->s6_addr[8], lladdr->hwaddr,
-					IEEE802154_ADDR_LEN);
-			/* second bit-flip (Universe/Local)
-			 * is done according RFC2464
-			 */
-			ipaddr->s6_addr[8] ^= 0x02;
-			break;
-		case IEEE802154_ADDR_SHORT:
-			/* fe:80::ff:fe00:XXXX
-			 *		  \__/
-			 *	       short_addr
-			 *
-			 * Universe/Local bit is zero.
-			 */
-			ipaddr->s6_addr[0] = 0xFE;
-			ipaddr->s6_addr[1] = 0x80;
-			ipaddr->s6_addr[11] = 0xFF;
-			ipaddr->s6_addr[12] = 0xFE;
-			ipaddr->s6_addr16[7] = htons(lladdr->short_addr);
-			break;
-		default:
-			pr_debug("Invalid addr_type set\n");
-			return -EINVAL;
-		}
-		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;
-	}
-
-	lowpan_raw_dump_inline(NULL, "Reconstructed ipv6 addr is:\n",
-			ipaddr->s6_addr, 16);
-
-	return 0;
-}
-
-/* Uncompress address function for source context
- * based address(non-multicast).
- */
-static int
-lowpan_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;
-	}
-
-	lowpan_raw_dump_inline(NULL,
-			"Reconstructed context based ipv6 src addr is:\n",
-			ipaddr->s6_addr, 16);
-
-	return 0;
-}
-
-/* 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 = lowpan_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 = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
-		fail |= lowpan_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 = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
-		fail |= lowpan_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 = lowpan_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;
-	}
-
-	lowpan_raw_dump_inline(NULL, "Reconstructed ipv6 multicast addr is:\n",
-			ipaddr->s6_addr, 16);
-
-	return 0;
-}
-
-static void
-lowpan_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
-lowpan_uncompress_udp_header(struct sk_buff *skb, struct udphdr *uh)
-{
-	u8 tmp;
-
-	if (!uh)
-		goto err;
-
-	if (lowpan_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;
-		}
-
-		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 lowpan_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;
 	const u8 *saddr = _saddr;
 	const u8 *daddr = _daddr;
-	u8 head[100];
 	struct ieee802154_addr sa, da;
 
 	/* TODO:
@@ -485,181 +149,14 @@ static int lowpan_header_create(struct sk_buff *skb,
 		return 0;
 
 	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);
-
-	lowpan_raw_dump_table(__func__, "raw skb network header dump",
-		skb_network_header(skb), sizeof(struct ipv6hdr));
 
 	if (!saddr)
 		saddr = dev->dev_addr;
 
 	lowpan_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 */
-
 	lowpan_raw_dump_inline(__func__, "daddr", (unsigned char *)daddr, 8);
 
-	/*
-	 * 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)
-		lowpan_compress_udp_header(&hc06_ptr, skb);
-
-	head[0] = iphc0;
-	head[1] = iphc1;
-
-	skb_pull(skb, sizeof(struct ipv6hdr));
-	skb_reset_transport_header(skb);
-	memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
-	skb_reset_network_header(skb);
-
-	lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data,
-				skb->len);
+	lowpan_header_compress(skb, dev, type, daddr, saddr, len);
 
 	/*
 	 * NOTE1: I'm still unsure about the fact that compression and WPAN
@@ -671,39 +168,38 @@ static int lowpan_header_create(struct sk_buff *skb,
 	 * from MAC subif of the 'dev' and 'real_dev' network devices, but
 	 * this isn't implemented in mainline yet, so currently we assign 0xff
 	 */
-	{
-		mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
-		mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
+	mac_cb(skb)->flags = IEEE802154_FC_TYPE_DATA;
+	mac_cb(skb)->seq = ieee802154_mlme_ops(dev)->get_dsn(dev);
 
-		/* prepare wpan address data */
-		sa.addr_type = IEEE802154_ADDR_LONG;
-		sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+	/* prepare wpan address data */
+	sa.addr_type = IEEE802154_ADDR_LONG;
+	sa.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
 
-		memcpy(&(sa.hwaddr), saddr, 8);
-		/* intra-PAN communications */
-		da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
+	memcpy(&(sa.hwaddr), saddr, 8);
+	/* intra-PAN communications */
+	da.pan_id = ieee802154_mlme_ops(dev)->get_pan_id(dev);
 
-		/*
-		 * if the destination address is the broadcast address, use the
-		 * corresponding short address
-		 */
-		if (lowpan_is_addr_broadcast(daddr)) {
-			da.addr_type = IEEE802154_ADDR_SHORT;
-			da.short_addr = IEEE802154_ADDR_BROADCAST;
-		} else {
-			da.addr_type = IEEE802154_ADDR_LONG;
-			memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN);
-
-			/* request acknowledgment */
-			mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
-		}
+	/*
+	 * if the destination address is the broadcast address, use the
+	 * corresponding short address
+	 */
+	if (lowpan_is_addr_broadcast(daddr)) {
+		da.addr_type = IEEE802154_ADDR_SHORT;
+		da.short_addr = IEEE802154_ADDR_BROADCAST;
+	} else {
+		da.addr_type = IEEE802154_ADDR_LONG;
+		memcpy(&(da.hwaddr), daddr, IEEE802154_ADDR_LEN);
 
-		return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
-				type, (void *)&da, (void *)&sa, skb->len);
+		/* request acknowledgment */
+		mac_cb(skb)->flags |= MAC_CB_FLAG_ACKREQ;
 	}
+
+	return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
+			type, (void *)&da, (void *)&sa, skb->len);
 }
 
-static int lowpan_give_skb_to_devices(struct sk_buff *skb)
+static int lowpan_give_skb_to_devices(struct sk_buff *skb,
+					struct net_device *dev)
 {
 	struct lowpan_dev_record *entry;
 	struct sk_buff *skb_cp;
@@ -726,31 +222,6 @@ static int lowpan_give_skb_to_devices(struct sk_buff *skb)
 	return stat;
 }
 
-static int lowpan_skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr)
-{
-	struct sk_buff *new;
-	int stat = NET_RX_SUCCESS;
-
-	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_copy_to_linear_data(new, hdr, sizeof(struct ipv6hdr));
-
-	new->protocol = htons(ETH_P_IPV6);
-	new->pkt_type = PACKET_HOST;
-
-	stat = lowpan_give_skb_to_devices(new);
-
-	kfree_skb(new);
-
-	return stat;
-}
-
 static void lowpan_fragment_timer_expired(unsigned long entry_addr)
 {
 	struct lowpan_fragment *entry = (struct lowpan_fragment *)entry_addr;
@@ -814,13 +285,10 @@ frame_err:
 	return NULL;
 }
 
-static int
-lowpan_process_data(struct sk_buff *skb)
+static int process_data(struct sk_buff *skb)
 {
-	struct ipv6hdr hdr = {};
-	u8 tmp, iphc0, iphc1, num_context = 0;
+	u8 iphc0, iphc1;
 	const struct ieee802154_addr *_saddr, *_daddr;
-	int err;
 
 	lowpan_raw_dump_table(__func__, "raw skb data dump", skb->data,
 				skb->len);
@@ -925,162 +393,11 @@ lowpan_process_data(struct sk_buff *skb)
 	_saddr = &mac_cb(skb)->sa;
 	_daddr = &mac_cb(skb)->da;
 
-	pr_debug("iphc0 = %02x, iphc1 = %02x\n", iphc0, iphc1);
-
-	/* another if the CID flag is set */
-	if (iphc1 & LOWPAN_IPHC_CID) {
-		pr_debug("CID flag is set, increase header with one\n");
-		if (lowpan_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 (lowpan_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 2: /* 10b */
-		if (lowpan_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 1: /* 01b */
-		if (lowpan_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 (lowpan_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 (lowpan_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 = lowpan_uncompress_context_based_src_addr(
-				skb, &hdr.saddr, tmp);
-	} else {
-		/* Source address uncompression */
-		pr_debug("source address stateless compression\n");
-		err = lowpan_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 = lowpan_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 (lowpan_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_copy_to_linear_data(skb, &uh, sizeof(struct udphdr));
-
-		lowpan_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);
-
-	lowpan_raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
-							sizeof(hdr));
-	return lowpan_skb_deliver(skb, &hdr);
+	return lowpan_process_data(skb, skb->dev, (u8 *)_saddr->hwaddr,
+				_saddr->addr_type, IEEE802154_ADDR_LEN,
+				(u8 *)_daddr->hwaddr, _daddr->addr_type,
+				IEEE802154_ADDR_LEN, iphc0, iphc1,
+				lowpan_give_skb_to_devices);
 
 unlock_and_drop:
 	spin_unlock_bh(&flist_lock);
@@ -1316,7 +633,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
 		/* Pull off the 1-byte of 6lowpan header. */
 		skb_pull(local_skb, 1);
 
-		lowpan_give_skb_to_devices(local_skb);
+		lowpan_give_skb_to_devices(local_skb, NULL);
 
 		kfree_skb(local_skb);
 		kfree_skb(skb);
@@ -1328,7 +645,7 @@ static int lowpan_rcv(struct sk_buff *skb, struct net_device *dev,
 			local_skb = skb_clone(skb, GFP_ATOMIC);
 			if (!local_skb)
 				goto drop;
-			lowpan_process_data(local_skb);
+			process_data(local_skb);
 
 			kfree_skb(skb);
 			break;
diff --git a/net/ieee802154/6lowpan.h b/net/ieee802154/6lowpan.h
index 2869c05..535606d 100644
--- a/net/ieee802154/6lowpan.h
+++ b/net/ieee802154/6lowpan.h
@@ -232,6 +232,28 @@
 					dest = 16 bit inline */
 #define LOWPAN_NHC_UDP_CS_P_11	0xF3 /* source & dest = 0xF0B + 4bit inline */
 
+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 inline bool lowpan_fetch_skb(struct sk_buff *skb,
 		void *data, const unsigned int len)
 {
@@ -244,4 +266,14 @@ static inline bool lowpan_fetch_skb(struct sk_buff *skb,
 	return false;
 }
 
+typedef int (*skb_delivery_cb)(struct sk_buff *skb, struct net_device *dev);
+
+int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
+		const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
+		const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
+		u8 iphc0, u8 iphc1, skb_delivery_cb skb_deliver);
+int lowpan_header_compress(struct sk_buff *skb, struct net_device *dev,
+			unsigned short type, const void *_daddr,
+			const void *_saddr, unsigned int len);
+
 #endif /* __6LOWPAN_H__ */
diff --git a/net/ieee802154/6lowpan_iphc.c b/net/ieee802154/6lowpan_iphc.c
new file mode 100644
index 0000000..57c0b7a
--- /dev/null
+++ b/net/ieee802154/6lowpan_iphc.c
@@ -0,0 +1,807 @@
+/*
+ * 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/* Jon's code is based on 6lowpan implementation for Contiki which is:
+ * Copyright (c) 2008, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <linux/bitops.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <net/ipv6.h>
+#include <net/af_ieee802154.h>
+
+#include "6lowpan.h"
+
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+				   unsigned char *buf, int len)
+{
+	if (msg)
+		pr_debug("%s():%s: ", caller, msg);
+	print_hex_dump_debug("", DUMP_PREFIX_NONE,
+		       16, 1, buf, len, false);
+}
+
+/*
+ * 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)
+{
+	if (msg)
+		pr_debug("%s():%s:\n", caller, msg);
+	print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET,
+		       16, 1, buf, len, 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, const u8 addr_type,
+				const u8 addr_len)
+{
+	bool fail;
+
+	switch (address_mode) {
+	case LOWPAN_IPHC_ADDR_00:
+		/* for global link addresses */
+		fail = lowpan_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 = lowpan_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 = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+		break;
+	case LOWPAN_IPHC_ADDR_03:
+		fail = false;
+		switch (addr_type) {
+		case IEEE802154_ADDR_LONG:
+			/* fe:80::XXXX:XXXX:XXXX:XXXX
+			 *        \_________________/
+			 *              hwaddr
+			 */
+			ipaddr->s6_addr[0] = 0xFE;
+			ipaddr->s6_addr[1] = 0x80;
+			memcpy(&ipaddr->s6_addr[8], lladdr, addr_len);
+			/* second bit-flip (Universe/Local)
+			 * is done according RFC2464
+			 */
+			ipaddr->s6_addr[8] ^= 0x02;
+			break;
+		case IEEE802154_ADDR_SHORT:
+			/* fe:80::ff:fe00:XXXX
+			 *		  \__/
+			 *	       short_addr
+			 *
+			 * Universe/Local bit is zero.
+			 */
+			ipaddr->s6_addr[0] = 0xFE;
+			ipaddr->s6_addr[1] = 0x80;
+			ipaddr->s6_addr[11] = 0xFF;
+			ipaddr->s6_addr[12] = 0xFE;
+			ipaddr->s6_addr16[7] = htons(*((u16 *)lladdr));
+			break;
+		default:
+			pr_debug("Invalid addr_type set\n");
+			return -EINVAL;
+		}
+		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;
+}
+
+static int skb_deliver(struct sk_buff *skb, struct ipv6hdr *hdr,
+		struct net_device *dev, skb_delivery_cb deliver_skb)
+{
+	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 = deliver_skb(new, dev);
+
+	kfree_skb(new);
+
+	return stat;
+}
+
+/* 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 = lowpan_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 = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+		fail |= lowpan_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 = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 1);
+		fail |= lowpan_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 = lowpan_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 (lowpan_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;
+}
+
+/* TTL uncompression values */
+static const u8 lowpan_ttl_values[] = { 0, 1, 64, 255 };
+
+int lowpan_process_data(struct sk_buff *skb, struct net_device *dev,
+		const u8 *saddr, const u8 saddr_type, const u8 saddr_len,
+		const u8 *daddr, const u8 daddr_type, const u8 daddr_len,
+		u8 iphc0, u8 iphc1, skb_delivery_cb deliver_skb)
+{
+	struct ipv6hdr hdr = {};
+	u8 tmp, num_context = 0;
+	int err;
+
+	raw_dump_table(__func__, "raw skb data dump uncompressed",
+				skb->data, skb->len);
+
+	/* another if the CID flag is set */
+	if (iphc1 & LOWPAN_IPHC_CID) {
+		pr_debug("CID flag is set, increase header with one\n");
+		if (lowpan_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 (lowpan_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 2: /* 10b */
+		if (lowpan_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 1: /* 01b */
+		if (lowpan_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 (lowpan_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 (lowpan_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,
+					saddr_type, saddr_len);
+	}
+
+	/* 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 {
+		err = uncompress_addr(skb, &hdr.daddr, tmp, daddr,
+					daddr_type, daddr_len);
+		pr_debug("dest: stateless compression mode %d dest %pI6c\n",
+			tmp, &hdr.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;
+	}
+
+	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\tdest    = %pI6c\n",
+		hdr.version, ntohs(hdr.payload_len), hdr.nexthdr,
+		hdr.hop_limit, &hdr.daddr);
+
+	raw_dump_table(__func__, "raw header dump", (u8 *)&hdr,
+							sizeof(hdr));
+
+	return skb_deliver(skb, &hdr, dev, deliver_skb);
+
+drop:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(lowpan_process_data);
+
+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 */
+		pr_debug("address compression 0 bits\n");
+	} 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 */
+		raw_dump_inline(NULL, "Compressed ipv6 addr is (16 bits)",
+			*hc06_ptr - 2, 2);
+	} else {
+		/* do not compress IID => xxxx::IID */
+		memcpy(*hc06_ptr, &ipaddr->s6_addr16[4], 8);
+		*hc06_ptr += 8;
+		val = 1; /* 64-bits */
+		raw_dump_inline(NULL, "Compressed ipv6 addr is (64 bits)",
+			*hc06_ptr - 8, 8);
+	}
+
+	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));
+}
+
+int lowpan_header_compress(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 head[100] = {};
+
+	if (type != ETH_P_IPV6)
+		return -EINVAL;
+
+	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\tdest    = %pI6c\n",
+		hdr->version, ntohs(hdr->payload_len), hdr->nexthdr,
+		hdr->hop_limit, &hdr->daddr);
+
+	raw_dump_table(__func__, "raw skb network header dump",
+		skb_network_header(skb), sizeof(struct ipv6hdr));
+
+	/*
+	 * 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__, "saddr",
+			(unsigned char *)_saddr, IEEE802154_ADDR_LEN);
+	raw_dump_inline(__func__, "daddr",
+			(unsigned char *)_daddr, IEEE802154_ADDR_LEN);
+
+	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)) {
+		iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+				LOWPAN_IPHC_SAM_BIT, &hdr->saddr, _saddr);
+		pr_debug("source address unicast link-local %pI6c "
+			"iphc1 0x%02x\n", &hdr->saddr, iphc1);
+	} 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)) {
+			iphc1 |= lowpan_compress_addr_64(&hc06_ptr,
+				LOWPAN_IPHC_DAM_BIT, &hdr->daddr, _daddr);
+			pr_debug("dest address unicast link-local %pI6c "
+				"iphc1 0x%02x\n", &hdr->daddr, iphc1);
+		} else {
+			pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
+			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));
+	skb_reset_transport_header(skb);
+	memcpy(skb_push(skb, hc06_ptr - head), head, hc06_ptr - head);
+	skb_reset_network_header(skb);
+
+	pr_debug("header len %d skb %u\n", (int)(hc06_ptr - head), skb->len);
+
+	raw_dump_table(__func__, "raw skb data dump compressed",
+				skb->data, skb->len);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(lowpan_header_compress);
diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile
index d7716d6..951a83e 100644
--- a/net/ieee802154/Makefile
+++ b/net/ieee802154/Makefile
@@ -1,5 +1,5 @@
 obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o
-obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o
+obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o 6lowpan_iphc.o
 
 ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o
 af_802154-y := af_ieee802154.o raw.o dgram.o
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v9 0/5] Bluetooth LE 6LoWPAN
From: Jukka Rissanen @ 2013-12-11  7:38 UTC (permalink / raw)
  To: linux-bluetooth

Hi,

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

v9:
- code style issues fixed in patch 4
- removed obsolete code from patch 5


v8:
- misc changes to patches 4 and 5 according to Marcel's
  comments
- added Alex's Acked-by to patch 1


v7:
- rebased on top of current bluetooth
- David Miller acked the patches 2 and 3 so adding Acked-by
  to those two patches


v6:
- Common IP header compression code for IEEE 802154 and Bluetooth
  moved to net/ieee802154/6lowpan_iphc.c. This is in patch 1 which
  is also sent separately to netdev ml.
- New ARPHRD type in patches 2 and 3 are also sent to netdev ml.
- fixes when counting number of 6lowpan peers (was not atomic)


v5:
- Moved the header compression functionality to net/core/6lowpan.c
  and rebased both BT and IEEE 802154 code to use it in patch 1.
  I will send a separate patch to net-next for comments.
- locking fixes
- debugfs handling moved to hci_core.c
- misc changes according to Marcel's comments


v4:
- removed the route setting code, neighbour discovery
  should allow the devices to discover each other
- fix the uncompression of Traffic Class in IPv6 header,
  this makes ssh to work between devices over a BT 6lowpan link
- removed setting of /proc conf options, they were useless
  and not to be done in kernel module anyway


v3:
- misc changes according to Marcel's comments
- supports multiple connections / interface
- removed unused fragmentation code
- setup 6lowpan connection automatically if enabled via debugfs

The automatic 6lowpan enabling is done by setting

echo 1 > /sys/kernel/debug/bluetooth/hci0/6lowpan

before devices are connected.


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 (UUID support)
- Enable/disable header compression for easier debugging

Known issues:
- no UUID handling yet


Cheers,
Jukka



Jukka Rissanen (5):
  6lowpan: Moving generic compression code into 6lowpan_iphc.c
  net: if_arp: add ARPHRD_6LOWPAN type
  ipv6: Add checks for 6LOWPAN ARP type
  Bluetooth: Enable 6LoWPAN support for BT LE devices
  Bluetooth: Manually enable or disable 6LoWPAN between devices

 include/net/bluetooth/hci.h      |   1 +
 include/net/bluetooth/hci_core.h |   1 +
 include/net/bluetooth/l2cap.h    |   1 +
 include/uapi/linux/if_arp.h      |   1 +
 net/bluetooth/6lowpan.c          | 874 +++++++++++++++++++++++++++++++++++++++
 net/bluetooth/6lowpan.h          |  26 ++
 net/bluetooth/Makefile           |   6 +-
 net/bluetooth/hci_core.c         |  53 +++
 net/bluetooth/hci_event.c        |   3 +
 net/bluetooth/l2cap_core.c       |  12 +
 net/ieee802154/6lowpan.c         | 753 ++-------------------------------
 net/ieee802154/6lowpan.h         |  32 ++
 net/ieee802154/6lowpan_iphc.c    | 807 ++++++++++++++++++++++++++++++++++++
 net/ieee802154/Makefile          |   2 +-
 net/ipv6/addrconf.c              |   4 +-
 15 files changed, 1855 insertions(+), 721 deletions(-)
 create mode 100644 net/bluetooth/6lowpan.c
 create mode 100644 net/bluetooth/6lowpan.h
 create mode 100644 net/ieee802154/6lowpan_iphc.c

-- 
1.8.3.1


^ permalink raw reply

* Re: [PATCH RFC] tty_ldisc: add more limits to the @write_wakeup
From: Huang Shijie @ 2013-12-11  6:44 UTC (permalink / raw)
  To: Peter Hurley; +Cc: gregkh, linux-serial, marcel, linux-bluetooth
In-Reply-To: <52A1F8BF.5030204@hurleysoftware.com>

=E4=BA=8E 2013=E5=B9=B412=E6=9C=8807=E6=97=A5 00:18, Peter Hurley =E5=86=99=
=E9=81=93:
> hci_uart_tx_wakeup() should perform the actual tx in a work item.=20
Does the "work item" mean a workqueue or a tasklet?
This patch is used to tell the line discipline writers to send the data=20
in the workqueue or a tasklet.

thanks
Huang Shijie

^ permalink raw reply

* Re: [PATCH v8 5/5] Bluetooth: Manually enable or disable 6LoWPAN between devices
From: Marcel Holtmann @ 2013-12-11  1:08 UTC (permalink / raw)
  To: Jukka Rissanen; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1386689689-26550-6-git-send-email-jukka.rissanen@linux.intel.com>

Hi Jukka,

> This is a temporary patch where user can manually enable or
> disable BT 6LoWPAN functionality between devices.
> Eventually the connection is established automatically if
> the devices are advertising suitable capability and this patch
> can be removed.
> 
> Before connecting the devices do this
> 
> echo Y > /sys/kernel/debug/bluetooth/hci0/6lowpan
> 
> This enables 6LoWPAN support and creates the bt0 interface
> automatically when devices are finally connected.
> 
> Rebooting or unloading the bluetooth kernel module will also clear the
> settings from the kernel.
> 
> Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
> ---
> net/bluetooth/hci_core.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 88 insertions(+)
> 
> diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
> index 8b8b5f8..924b87a 100644
> --- a/net/bluetooth/hci_core.c
> +++ b/net/bluetooth/hci_core.c
> @@ -636,6 +636,92 @@ static int conn_max_interval_get(void *data, u64 *val)
> DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
> 			conn_max_interval_set, "%llu\n");
> 
> +static LIST_HEAD(lowpan_user_enabled);
> +static DEFINE_RWLOCK(lowpan_enabled_lock);
> +
> +struct lowpan_enabled {
> +	__u16 dev_id;
> +	bool enabled;
> +	struct list_head list;
> +};

I still do not get why we need this.

> +
> +static ssize_t lowpan_read(struct file *file, char __user *user_buf,
> +			   size_t count, loff_t *ppos)
> +{
> +	struct hci_dev *hdev = file->private_data;
> +	char buf[3];
> +
> +	buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y': 'N';
> +	buf[1] = '\n';
> +	buf[2] = '\0';
> +	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
> +}
> +
> +static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
> +			    size_t count, loff_t *position)
> +{
> +	struct hci_dev *hdev = fp->private_data;
> +	struct lowpan_enabled *entry = NULL, *tmp;
> +	bool new_value, old_value;
> +	char buf[32];
> +	size_t buf_size = min(count, (sizeof(buf)-1));
> +	ssize_t ret;
> +
> +	BT_DBG("dev %s count %zd", hdev->dev_name, count);
> +
> +	if (copy_from_user(buf, user_buffer, buf_size))
> +		return -EFAULT;
> +
> +	buf[buf_size] = '\0';
> +
> +	if (strtobool(buf, &new_value) < 0)
> +		return -EINVAL;
> +
> +	ret = -ENOENT;
> +
> +	write_lock(&lowpan_enabled_lock);
> +	list_for_each_entry_safe(entry, tmp, &lowpan_user_enabled, list) {
> +		if (entry->dev_id == hdev->id) {
> +			old_value = entry->enabled;
> +			entry->enabled = new_value;
> +			ret = 0;
> +			break;
> +		}
> +	}
> +	write_unlock(&lowpan_enabled_lock);
> +
> +	if (ret == 0 && old_value == new_value)
> +		return count;
> +
> +	if (ret < 0) {
> +		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +		if (!entry)
> +			return -ENOMEM;
> +
> +		entry->dev_id = hdev->id;
> +		entry->enabled = new_value;
> +
> +		write_lock(&lowpan_enabled_lock);
> +		INIT_LIST_HEAD(&entry->list);
> +		list_add(&entry->list, &lowpan_user_enabled);
> +		write_unlock(&lowpan_enabled_lock);
> +	}
> +
> +	if (new_value == true)
> +		set_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
> +	else
> +		clear_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);

Why not just set or clear the HCI_6LOWPAN_ENABLED bit and be done with it.

Especially since the current code leaks memory. I do not see any function freeing it. Do I miss something so obvious here?

> +
> +	return count;
> +}
> +
> +static const struct file_operations lowpan_debugfs_fops = {
> +	.open		= simple_open,
> +	.read		= lowpan_read,
> +	.write		= lowpan_write,
> +	.llseek		= default_llseek,
> +};
> +
> /* ---- HCI requests ---- */
> 
> static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
> @@ -1406,6 +1492,8 @@ static int __hci_init(struct hci_dev *hdev)
> 				    hdev, &conn_min_interval_fops);
> 		debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
> 				    hdev, &conn_max_interval_fops);
> +		debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
> +				    &lowpan_debugfs_fops);
> 	}

Regards

Marcel


^ permalink raw reply

* Re: [PATCH v8 4/5] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Marcel Holtmann @ 2013-12-11  1:02 UTC (permalink / raw)
  To: Jukka Rissanen; +Cc: linux-bluetooth@vger.kernel.org development
In-Reply-To: <1386689689-26550-5-git-send-email-jukka.rissanen@linux.intel.com>

Hi Jukka,

> This is initial version of
> http://tools.ietf.org/html/draft-ietf-6lo-btle-00
> 
> By default the 6LoWPAN support is not activated and user
> needs to tweak /sys/kernel/debug/bluetooth/hci0/6lowpan
> file.
> 
> The kernel needs IPv6 support before 6LoWPAN is usable.
> 
> Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
> ---
> include/net/bluetooth/hci.h      |   1 +
> include/net/bluetooth/hci_core.h |   1 +
> include/net/bluetooth/l2cap.h    |   1 +
> net/bluetooth/6lowpan.c          | 880 +++++++++++++++++++++++++++++++++++++++
> net/bluetooth/6lowpan.h          |  26 ++
> net/bluetooth/Makefile           |   6 +-
> net/bluetooth/hci_event.c        |   3 +
> net/bluetooth/l2cap_core.c       |  12 +
> 8 files changed, 929 insertions(+), 1 deletion(-)
> create mode 100644 net/bluetooth/6lowpan.c
> create mode 100644 net/bluetooth/6lowpan.h
> 
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index cc2da73..5dc3d90 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -131,6 +131,7 @@ enum {
> 	HCI_PERIODIC_INQ,
> 	HCI_FAST_CONNECTABLE,
> 	HCI_BREDR_ENABLED,
> +	HCI_6LOWPAN_ENABLED,
> };
> 
> /* A mask for the flags that are supposed to remain when a reset happens
> diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
> index b796161..f2f0cf5 100644
> --- a/include/net/bluetooth/hci_core.h
> +++ b/include/net/bluetooth/hci_core.h
> @@ -448,6 +448,7 @@ enum {
> 	HCI_CONN_SSP_ENABLED,
> 	HCI_CONN_POWER_SAVE,
> 	HCI_CONN_REMOTE_OOB,
> +	HCI_CONN_6LOWPAN,
> };
> 
> static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
> diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
> index e149e99..dbc4a89 100644
> --- a/include/net/bluetooth/l2cap.h
> +++ b/include/net/bluetooth/l2cap.h
> @@ -136,6 +136,7 @@ struct l2cap_conninfo {
> #define L2CAP_FC_L2CAP		0x02
> #define L2CAP_FC_CONNLESS	0x04
> #define L2CAP_FC_A2MP		0x08
> +#define L2CAP_FC_6LOWPAN        0x3e /* reserved and temporary value */
> 
> /* 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..5da0f99
> --- /dev/null
> +++ b/net/bluetooth/6lowpan.c
> @@ -0,0 +1,880 @@
> +/*
> +   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/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> /* to get the address type */
> +
> +#include <net/bluetooth/bluetooth.h>
> +#include <net/bluetooth/hci_core.h>
> +#include <net/bluetooth/l2cap.h>
> +
> +#include "../ieee802154/6lowpan.h" /* for the compression support */
> +
> +#define IFACE_NAME_TEMPLATE "bt%d"
> +#define EUI64_ADDR_LEN 8
> +
> +struct skb_cb {
> +	struct in6_addr addr;
> +	struct l2cap_conn *conn;
> +};
> +#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
> +
> +/*
> + * 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 network (=hciX device).
> + * The list contains struct lowpan_dev elements.
> + */
> +static LIST_HEAD(bt_6lowpan_devices);
> +static DEFINE_RWLOCK(devices_lock);
> +
> +struct lowpan_dev {
> +	struct net_device *dev;
> +	struct work_struct delete_netdev;
> +	struct list_head list;
> +};
> +
> +struct lowpan_peer {
> +	struct list_head list;
> +	struct l2cap_conn *conn;
> +
> +	/* peer addresses in various formats */
> +	unsigned char eui64_addr[EUI64_ADDR_LEN];
> +	struct in6_addr peer_addr;
> +};
> +
> +struct lowpan_info {
> +	struct net_device *net;
> +	struct list_head peers;
> +	atomic_t peer_count; /* number of items in peers list */
> +
> +	struct delayed_work notify_peers;
> +};
> +
> +static inline struct lowpan_info *lowpan_info(const struct net_device *dev)
> +{
> +	return netdev_priv(dev);
> +}
> +
> +static inline void peer_add(struct lowpan_info *info, struct lowpan_peer *peer)
> +{
> +	list_add(&peer->list, &info->peers);
> +	atomic_inc(&info->peer_count);
> +}
> +
> +static inline bool peer_del(struct lowpan_info *info, struct lowpan_peer *peer)
> +{
> +	list_del(&peer->list);
> +
> +	if (atomic_dec_and_test(&info->peer_count)) {
> +		BT_DBG("last peer");
> +		return true;
> +	}
> +
> +	return false;
> +}
> +
> +static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_info *info,
> +						 bdaddr_t *ba, __u8 type)
> +{
> +	struct lowpan_peer *peer, *tmp;
> +
> +	BT_DBG("peers %d addr %pMR type %d", atomic_read(&info->peer_count),
> +					     ba, type);
> +
> +	list_for_each_entry_safe(peer, tmp, &info->peers, list) {
> +		BT_DBG("addr %pMR type %d",
> +			&peer->conn->hcon->dst, peer->conn->hcon->dst_type);
> +
> +		if (!bacmp(&peer->conn->hcon->dst, ba))
> +			return peer;
> +	}
> +
> +	return NULL;
> +}
> +
> +static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_info *info,
> +						   struct l2cap_conn *conn)
> +{
> +	struct lowpan_peer *peer, *tmp;
> +
> +	list_for_each_entry_safe(peer, tmp, &info->peers, list) {
> +		if (peer->conn == conn)
> +			return peer;
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn,
> +				       struct lowpan_info **dev)
> +{
> +	struct lowpan_dev *entry, *tmp;
> +	struct lowpan_peer *peer = NULL;
> +	unsigned long flags;
> +
> +	read_lock_irqsave(&devices_lock, flags);
> +
> +	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
> +		struct lowpan_info *info = lowpan_info(entry->dev);
> +
> +		peer = peer_lookup_conn(info, conn);
> +		if (peer) {
> +			if (dev)
> +				*dev = info;
> +			break;
> +		}
> +	}
> +
> +	read_unlock_irqrestore(&devices_lock, flags);
> +
> +	return peer;
> +}
> +
> +/* print data in line */
> +static inline void raw_dump_inline(const char *caller, char *msg,
> +				   unsigned char *buf, int len)
> +{
> +	if (msg)
> +		pr_debug("%s():%s: ", caller, msg);
> +	print_hex_dump_debug("", DUMP_PREFIX_NONE,
> +		       16, 1, buf, len, false);

please fix the indentation here to comply with the coding style.

> +}
> +
> +/*
> + * 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)
> +{
> +	if (msg)
> +		pr_debug("%s():%s:\n", caller, msg);
> +	print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET,
> +		       16, 1, buf, len, false);

Same here.

> +}
> +
> +static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct sk_buff *skb_cp;
> +	int ret;
> +
> +	skb_cp = skb_copy(skb, GFP_ATOMIC);
> +	if (!skb_cp)
> +		return -ENOMEM;
> +
> +	ret = netif_rx(skb_cp);
> +
> +	BT_DBG("receive skb %d", ret);
> +	if (ret < 0)
> +		return NET_RX_DROP;
> +
> +	return ret;
> +}
> +
> +static int process_data(struct sk_buff *skb, struct net_device *dev,
> +			struct l2cap_conn *conn)
> +{
> +	const u8 *saddr, *daddr;
> +	u8 iphc0, iphc1;
> +	struct lowpan_info *info;
> +	struct lowpan_peer *peer;
> +	unsigned long flags;
> +
> +	info = lowpan_info(dev);
> +
> +	read_lock_irqsave(&devices_lock, flags);
> +	peer = peer_lookup_conn(info, conn);
> +	read_unlock_irqrestore(&devices_lock, flags);
> +	if (!peer)
> +		return -EINVAL;

Are we not leaking the skb here?

> +
> +	saddr = peer->eui64_addr;
> +	daddr = info->net->dev_addr;
> +
> +	/* at least two bytes will be used for the encoding */
> +	if (skb->len < 2)
> +		goto drop;
> +
> +	if (lowpan_fetch_skb_u8(skb, &iphc0))
> +		goto drop;
> +
> +	if (lowpan_fetch_skb_u8(skb, &iphc1))
> +		goto drop;
> +
> +	return lowpan_process_data(skb, dev,
> +				saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
> +				daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
> +				iphc0, iphc1, give_skb_to_upper);

The indentation seems wrong here as well.

> +
> +drop:
> +	kfree_skb(skb);
> +	return -EINVAL;
> +}
> +
> +static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
> +		    struct l2cap_conn *conn)
> +{
> +	struct sk_buff *local_skb;
> +
> +	if (!netif_running(dev))
> +		goto drop;
> +
> +	if (dev->type != ARPHRD_6LOWPAN)
> +		goto drop;
> +
> +	/* 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 */
> +			local_skb = skb_clone(skb, GFP_ATOMIC);
> +			if (!local_skb)
> +				goto drop;
> +			if (process_data(local_skb, dev, conn)
> +							!= 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;
> +}
> +
> +/* Packet from BT LE device */
> +int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
> +{
> +	struct lowpan_info *info = NULL;
> +	struct lowpan_peer *peer;
> +	int err;
> +
> +	peer = lookup_peer(conn, &info);
> +	if (!peer)
> +		return -ENOENT;
> +
> +	if (!info->net)
> +		return -ENOENT;
> +
> +	err = recv_pkt(skb, info->net, conn);
> +	BT_DBG("recv pkt %d", err);
> +
> +	return err;
> +}
> +
> +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);
> +}
> +
> +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_ATOMIC);
> +		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);

You need to indent this properly.

> +
> +		(*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;
> +
> +	/*
> +	 * XXX: This mtu check should be not needed and atm is only used for
> +	 * testing purposes
> +	 */

Please use the correct style for multi-line comments. And I prefer using FIXME instead of XXX.

> +	if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
> +		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_ATOMIC);
> +	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;
> +
> +	skb = create_pdu(conn, msg, len, priority, dev);
> +	if (IS_ERR(skb))
> +		return -EINVAL;
> +
> +	do_send(conn, skb);
> +	return 0;
> +}
> +
> +static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
> +			    bdaddr_t *addr, u8 *addr_type)
> +{
> +	u8 *eui64;
> +
> +	eui64 = ip6_daddr->s6_addr + 8;
> +
> +	addr->b[0] = eui64[7];
> +	addr->b[1] = eui64[6];
> +	addr->b[2] = eui64[5];
> +	addr->b[3] = eui64[2];
> +	addr->b[4] = eui64[1];
> +	addr->b[5] = eui64[0];
> +
> +	addr->b[5] ^= 2;
> +
> +	/* Set universal/local bit to 0 */
> +	if (addr->b[5] & 1) {
> +		addr->b[5] &= ~1;
> +		*addr_type = BDADDR_LE_PUBLIC;
> +	} else
> +		*addr_type = BDADDR_LE_RANDOM;

Use else { } here as well. If one branch uses it, both should use it.

> +}
> +
> +static int header_create(struct sk_buff *skb, struct net_device *dev,
> +		         unsigned short type, const void *_daddr,
> +		         const void *_saddr, unsigned int len)
> +{
> +	struct ipv6hdr *hdr;
> +	struct lowpan_info *info;
> +	struct lowpan_peer *peer;
> +	bdaddr_t addr, *any = BDADDR_ANY;
> +	u8 *saddr, *daddr = any->b;
> +	u8 addr_type;
> +
> +	if (type != ETH_P_IPV6)
> +		return -EINVAL;
> +
> +	hdr = ipv6_hdr(skb);
> +
> +	info = lowpan_info(dev);
> +
> +	if (ipv6_addr_is_multicast(&hdr->daddr)) {
> +		memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
> +		       sizeof(struct in6_addr));
> +		lowpan_cb(skb)->conn = NULL;
> +	} else {
> +		unsigned long flags;
> +
> +		/*
> +		 * Get destination BT device from skb.
> +		 * If there is no such peer then discard the packet.
> +		 */
> +		get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
> +
> +		BT_DBG("dest addr %pMR type %d", &addr, addr_type);
> +
> +		read_lock_irqsave(&devices_lock, flags);
> +		peer = peer_lookup_ba(info, &addr, addr_type);
> +		read_unlock_irqrestore(&devices_lock, flags);
> +
> +		if (!peer) {
> +			BT_DBG("no such peer %pMR found", &addr);
> +			return -ENOENT;
> +		}
> +
> +		daddr = peer->eui64_addr;
> +
> +		memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
> +						sizeof(struct in6_addr));
> +		lowpan_cb(skb)->conn = peer->conn;
> +	}
> +
> +	saddr = info->net->dev_addr;
> +
> +	return lowpan_header_compress(skb, dev, type, daddr, saddr, len);
> +}
> +
> +/* 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);

Indentation.

> +
> +	return conn_send(conn, skb->data, skb->len, 0, dev);
> +}
> +
> +static int send_mcast_pkt(struct sk_buff *skb, struct net_device *dev)
> +{
> +	struct sk_buff *local_skb;
> +	struct lowpan_dev *entry, *tmp;
> +	int err = 0;
> +	unsigned long flags;
> +
> +	read_lock_irqsave(&devices_lock, flags);
> +
> +	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
> +		struct lowpan_peer *pentry, *ptmp;
> +		struct lowpan_info *info;
> +
> +		if (entry->dev != dev)
> +			continue;
> +
> +		info = lowpan_info(entry->dev);
> +
> +		list_for_each_entry_safe(pentry, ptmp, &info->peers, list) {
> +			local_skb = skb_clone(skb, GFP_ATOMIC);
> +
> +			err = send_pkt(pentry->conn, dev->dev_addr,
> +				       pentry->eui64_addr,
> +				       local_skb, dev);
> +
> +			kfree_skb(local_skb);
> +		}
> +	}
> +
> +	read_unlock_irqrestore(&devices_lock, flags);
> +
> +	return err;
> +}
> +
> +static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *dev)
> +{
> +	int err = -ENOENT;
> +	unsigned char *eui64_addr;
> +	struct lowpan_info *info;
> +	struct lowpan_peer *peer;
> +	bdaddr_t addr;
> +	u8 addr_type;
> +
> +	if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) {
> +		/*
> +		 * We need to send the packet to every device
> +		 * behind this interface.
> +		 */

Multi-line comment style.

> +		err = send_mcast_pkt(skb, dev);
> +	} else {
> +		unsigned long flags;
> +
> +		get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
> +		eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
> +		info = lowpan_info(dev);
> +
> +		read_lock_irqsave(&devices_lock, flags);
> +		peer = peer_lookup_ba(info, &addr, addr_type);
> +		read_unlock_irqrestore(&devices_lock, flags);
> +
> +		BT_DBG("xmit from %s to %pMR (%pI6c), peer %p", dev->name,
> +			&addr, &lowpan_cb(skb)->addr, peer);
> +
> +		if (peer && peer->conn)
> +			err = send_pkt(peer->conn, dev->dev_addr, eui64_addr,
> +					skb, dev);
> +	}
> +	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		= bt_xmit,
> +};
> +
> +static struct header_ops header_ops = {
> +	.create	= header_create,
> +};
> +
> +static void netdev_setup(struct net_device *dev)
> +{
> +	dev->addr_len		= EUI64_ADDR_LEN;
> +	dev->type		= ARPHRD_6LOWPAN;
> +
> +	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->header_ops		= &header_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;
> +
> +	/* Universal/local bit set, RFC 4291 */
> +	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 err;
> +
> +	rtnl_lock();
> +	err = dev_open(net);
> +	if (err < 0)
> +		BT_INFO("iface %s cannot be opened (%d)", net->name, err);
> +	rtnl_unlock();
> +}
> +
> +static void do_notify_peers(struct work_struct *work)
> +{
> +	struct lowpan_info *info = container_of(work, struct lowpan_info,
> +						notify_peers.work);
> +
> +	netdev_notify_peers(info->net); /* send neighbour adv at startup */
> +}
> +
> +static bool is_bt_6lowpan(struct hci_conn *hcon)
> +{
> +	if (hcon->type != LE_LINK)
> +		return false;
> +
> +	return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
> +}
> +
> +/*
> + * 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 lowpan_info *dev = NULL;
> +	struct lowpan_peer *peer = NULL;
> +	struct net_device *net;
> +	struct lowpan_dev *entry;
> +	int err = 0;
> +	unsigned long flags;
> +
> +	if (!is_bt_6lowpan(conn->hcon))
> +		return 0;
> +
> +	peer = lookup_peer(conn, &dev);
> +	if (peer)
> +		return -EEXIST;
> +
> +	if (dev && !peer)
> +		goto add_peer;
> +
> +	net = alloc_netdev(sizeof(struct lowpan_info), IFACE_NAME_TEMPLATE,
> +		           netdev_setup);
> +	if (!net)
> +		return -ENOMEM;
> +
> +	dev = netdev_priv(net);
> +	dev->net = net;
> +	INIT_LIST_HEAD(&dev->peers);
> +
> +	set_dev_addr(net, &conn->hcon->src, conn->hcon->src_type);
> +
> +	net->netdev_ops = &netdev_ops;
> +	SET_NETDEV_DEV(net, &conn->hcon->dev);
> +	SET_NETDEV_DEVTYPE(net, &bt_type);
> +
> +	err = register_netdev(net);
> +	if (err < 0) {
> +		BT_INFO("register_netdev failed %d", err);
> +		free_netdev(net);
> +		goto out;
> +	}
> +
> +	BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
> +		net->ifindex, &conn->hcon->dst, &conn->hcon->src);
> +	set_bit(__LINK_STATE_PRESENT, &net->state);
> +
> +	entry = kzalloc(sizeof(struct lowpan_dev), GFP_ATOMIC);
> +	if (!entry) {
> +		unregister_netdev(net);
> +		return -ENOMEM;
> +	}
> +
> +	entry->dev = net;
> +
> +	write_lock_irqsave(&devices_lock, flags);
> +	INIT_LIST_HEAD(&entry->list);
> +	list_add(&entry->list, &bt_6lowpan_devices);
> +	write_unlock_irqrestore(&devices_lock, flags);
> +
> +	ifup(net);
> +
> +add_peer:
> +	peer = kzalloc(sizeof(struct lowpan_peer), GFP_ATOMIC);
> +	if (!peer)
> +		return -ENOMEM;
> +
> +	peer->conn = conn;
> +	memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
> +
> +	/* RFC 2464 ch. 5 */
> +	peer->peer_addr.s6_addr[0] = 0xFE;
> +	peer->peer_addr.s6_addr[1] = 0x80;
> +	set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
> +		conn->hcon->dst_type);
> +
> +	memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
> +		EUI64_ADDR_LEN);
> +	peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
> +				   * is done according RFC2464
> +				   */
> +
> +	raw_dump_inline(__func__, "peer IPv6 address",
> +			(unsigned char *)&peer->peer_addr, 16);
> +	raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
> +
> +	write_lock_irqsave(&devices_lock, flags);
> +	INIT_LIST_HEAD(&peer->list);
> +	peer_add(dev, peer);
> +	write_unlock_irqrestore(&devices_lock, flags);
> +
> +	/*
> +	 * Notifying peers about us needs to be done without locks held
> +	 */

Single line comment.

> +	INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
> +	schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
> +
> +out:
> +	return err;
> +}
> +
> +static void delete_netdev(struct work_struct *work)
> +{
> +	struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
> +						delete_netdev);
> +
> +	unregister_netdev(entry->dev);
> +
> +	/* The entry pointer is deleted in device_event() */
> +}
> +
> +int bt_6lowpan_del_conn(struct l2cap_conn *conn)
> +{
> +	struct lowpan_dev *entry, *tmp;
> +	struct lowpan_info *info = NULL;
> +	struct lowpan_peer *peer;
> +	int err = -ENOENT;
> +	unsigned long flags;
> +	bool last = false;
> +
> +	if (!is_bt_6lowpan(conn->hcon))
> +		return 0;
> +
> +	write_lock_irqsave(&devices_lock, flags);
> +
> +	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
> +		info = lowpan_info(entry->dev);
> +		peer = peer_lookup_conn(info, conn);
> +		if (peer) {
> +			last = peer_del(info, peer);
> +			err = 0;
> +			break;
> +		}
> +	}
> +
> +	if (!err && last && info && !atomic_read(&info->peer_count)) {
> +		write_unlock_irqrestore(&devices_lock, flags);
> +
> +		cancel_delayed_work_sync(&info->notify_peers);
> +
> +		/*
> +		 * bt_6lowpan_del_conn() is called with hci dev lock held which
> +		 * means that we must delete the netdevice in worker thread.
> +		 */

Multi-line comment style.

> +		INIT_WORK(&entry->delete_netdev, delete_netdev);
> +		schedule_work(&entry->delete_netdev);
> +	} else
> +		write_unlock_irqrestore(&devices_lock, flags);
> +
> +	return err;
> +}
> +
> +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;
> +	unsigned long flags;
> +
> +	if (dev->type != ARPHRD_6LOWPAN)
> +		return NOTIFY_DONE;
> +
> +	switch (event) {
> +	case NETDEV_UNREGISTER:
> +		write_lock_irqsave(&devices_lock, flags);
> +		list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
> +					list) {
> +			if (entry->dev == dev) {
> +				list_del(&entry->list);
> +				kfree(entry);
> +				break;
> +			}
> +		}
> +		write_unlock_irqrestore(&devices_lock, flags);
> +		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..cc6827e 100644
> --- a/net/bluetooth/Makefile
> +++ b/net/bluetooth/Makefile
> @@ -10,6 +10,10 @@ 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
> +
> +ifeq ($(CONFIG_IEEE802154_6LOWPAN),)
> +  bluetooth-y +=  ../ieee802154/6lowpan_iphc.o
> +endif
> 
> subdir-ccflags-y += -D__CHECK_ENDIAN__
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index 5fb3df6..5f81245 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
> 	conn->handle = __le16_to_cpu(ev->handle);
> 	conn->state = BT_CONNECTED;
> 
> +	if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
> +		set_bit(HCI_CONN_6LOWPAN, &conn->flags);
> +
> 	hci_conn_add_sysfs(conn);
> 
> 	hci_proto_connect_cfm(conn, ev->status);
> diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
> index ae0054c..e97248d 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;
> 
> @@ -1468,6 +1469,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
> 
> 	BT_DBG("");
> 
> +	bt_6lowpan_add_conn(conn);
> +
> 	/* Check if we have socket listening on cid */
> 	pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
> 					  &hcon->src, &hcon->dst);
> @@ -7093,6 +7096,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;
> @@ -7160,6 +7167,8 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
> {
> 	BT_DBG("hcon %p reason %d", hcon, reason);
> 
> +	bt_6lowpan_del_conn(hcon->l2cap_data);
> +
> 	l2cap_conn_del(hcon, bt_to_errno(reason));
> }
> 
> @@ -7441,11 +7450,14 @@ int __init l2cap_init(void)
> 	debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
> 			   &le_default_mps);
> 
> +	bt_6lowpan_init();
> +
> 	return 0;
> }
> 
> void l2cap_exit(void)
> {
> +	bt_6lowpan_cleanup();
> 	debugfs_remove(l2cap_debugfs);
> 	l2cap_cleanup_sockets();
> }

Regards

Marcel


^ permalink raw reply

* Re: Unknown option 'pin_helper
From: valent.turkovic @ 2013-12-10 19:46 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <CAD=nrE_R6h++1AofwTHoHjRT=YOc1VPUaic7AYO_jbh6Eb_koQ@mail.gmail.com>

On Tue, Dec 10, 2013 at 8:39 PM, valent.turkovic@gmail.com
<valent.turkovic@gmail.com> wrote:
> Hi,
> on OpenWrt latest version of bluez-utils is still 3.36 and when I add
> pin_helper to hcid.conf file I see this error when starting hcid
> daemon:
>
> # hcid -n
> hcid[1063]: Bluetooth HCI daemon
> hcid[1063]: Unknown option 'pin_helper' line 26
>
>
> Should bluez-utils 3.36 have support for pin_helper by default? I have
> checked source code of OpenWrt and can't find any patch that disables
> pin_helper.
>
> What could the issue here?

Ah, I was already searching day ago for this issue an by Murphy's Law
as soon as email was send I found an answer:

"the "pin_helper" option is only valid with bluez-libs-2.x &
bluez-utils-2.x and below.
Thats what is causing the service to fail to start up.
Bluez-3.X has replaced pin helpers with passkey agents such as
kbluetoothd and bluez-gnome, the bluez-utils also comes with an
command line agent passkey-agent. "

Can anybody please point me in the direction of documentation that
explains how to use passkey agents on bluez-utils 3.x and later?

I saw that BlueZ website had wiki but that it is no longer available.

^ permalink raw reply

* Unknown option 'pin_helper
From: valent.turkovic @ 2013-12-10 19:39 UTC (permalink / raw)
  To: linux-bluetooth

Hi,
on OpenWrt latest version of bluez-utils is still 3.36 and when I add
pin_helper to hcid.conf file I see this error when starting hcid
daemon:

# hcid -n
hcid[1063]: Bluetooth HCI daemon
hcid[1063]: Unknown option 'pin_helper' line 26


Should bluez-utils 3.36 have support for pin_helper by default? I have
checked source code of OpenWrt and can't find any patch that disables
pin_helper.

What could the issue here?

^ permalink raw reply

* Re: Help for USB Dongle Asus USB-BT400 with Raspberry Pi and Bluez
From: Markus Roppelt @ 2013-12-10 18:25 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <CALzzRXk2F_3DKysS2SphGw-2WPqK-karYv+YqRiOC75=UJCHhw@mail.gmail.com>

Hi Alessandro,

you should check what kernel you are running on your raspi and your
notebook (uname -a)
You can update your kernel and the other firmware on the raspi with rpi-update.

Can you also look at the kernel messages (with dmesg), especially for
bluetooth messages, on both machines.

Hope this helps to narrow the problem down.

Greetings
 Markus


2013/12/2 Alessandro Nicolosi <alenico84@gmail.com>:
> Hi guys,
>
> I hope this can be the right place where to find help for my problems.
>
> I need to use the dongle Asus USB-BT400 to provide Bluetooth support
> for my Raspberry Pi in which I installed the Raspbian distribution.
> I have to work with Raspberry Pi and a BLE device called SensorTag
> from Texas Instruments. So I have to use the gatttool command from
> Bluez.
> I followed the instructions from this site to set up Bluez in my
> Raspberry Pi with Raspbian.
>
>  http://mike.saunby.net/2013/04/raspberry-pi-and-ti-cc2541-sensortag.html
>
> I downloaded and installed the last version of Bluez 5.11.
> I plugged in my BLE USB dongle in my Raspberry Pi but I think there
> are some problems.
> When I type in the command "hciconfig" I have no output results.
> And also when I type in "hcitool dev" no devices is listed.
> But when I type in "lsusb" the output is:
>
> Bus 001 Device 002: ID 0424:9512 Standard Microsystems Corp.
> Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
> Bus 001 Device 003: ID 0424:ec00 Standard Microsystems Corp.
> Bus 001 Device 004: ID 0b05:17cb ASUSTek Computer, Inc.
>
> The last one is my USB dongle, so I think the hardware is correctly recognized.
> Which could be my problem?
>
> I also tried the dongle Asus USB-BT400 in my notebook with Xubuntu
> 13.10 and it worked very well (hciconfig sees the bluetooth
> interface).
>
> I also read something about my dongle by googling and I found the
> message linked below from this mailing list, so I thought you can help
> me or give me a way forward.
>
> http://marc.info/?l=linux-bluetooth&m=137812308709357&w=2
>
> Maybe I can edit some source files to add the device ID? If yes, which
> changes I have to do?
>
> Thanks in advance for your help
> Regards
>
> --
> Alessandro Nicolosi
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* pull request: bluetooth-next 2013-12-10
From: Gustavo Padovan @ 2013-12-10 15:38 UTC (permalink / raw)
  To: linville; +Cc: linux-wireless, linux-bluetooth, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 3804 bytes --]

Hi John,

More patches to 3.14. Here we have the support for Low Energy Connection
Oriented Channels (LE CoC). Basically, as the name says, this adds supports
for connection oriented channels in the same way we already have them for
BR/EDR connections so profiles/protocols that work on top of BR/EDR can now
work on LE plus a plenty of new possibilities for LE.

Please pull. Thanks!!

	Gustavo

---
The following changes since commit 201a5929c8c788f9ef53b010065c9ce70c9c06f0:

  Bluetooth: Remove dead code from SMP encryption function (2013-12-04 11:09:05 -0200)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git for-upstream

for you to fetch changes up to 71fb419724fadab4efdf98210aa3fe053bd81d29:

  Bluetooth: Fix handling of L2CAP Command Reject over LE (2013-12-10 01:15:44 -0800)

----------------------------------------------------------------
Johan Hedberg (35):
      Bluetooth: Remove unnecessary braces from one-line if-statement
      Bluetooth: Add module parameter to enable LE CoC support
      Bluetooth: Update l2cap_global_chan_by_psm() to take a link type
      Bluetooth: Allow l2cap_chan_check_security() to be used for LE links.
      Bluetooth: Pass command length to LE signaling channel handlers
      Bluetooth: Move LE L2CAP initiator procedure to its own function
      Bluetooth: Add definitions for LE connection oriented channels
      Bluetooth: Add initial code for LE L2CAP Connect Request
      Bluetooth: Add smp_sufficient_security helper function
      Bluetooth: Refactor L2CAP connect rejection to its own function
      Bluetooth: Add basic LE L2CAP connect request receiving support
      Bluetooth: Fix L2CAP channel closing for LE connections
      Bluetooth: Add L2CAP Disconnect suppport for LE
      Bluetooth: Make l2cap_le_sig_cmd logic consistent
      Bluetooth: Add LE L2CAP flow control mode
      Bluetooth: Track LE L2CAP credits in l2cap_chan
      Bluetooth: Limit L2CAP_OPTIONS socket option usage with LE
      Bluetooth: Add new BT_SNDMTU and BT_RCVMTU socket options
      Bluetooth: Implement returning of LE L2CAP credits
      Bluetooth: Add LE flow control discipline
      Bluetooth: Reject LE CoC commands when the feature is not enabled
      Bluetooth: Introduce L2CAP channel callback for suspending
      Bluetooth: Add LE L2CAP segmentation support for outgoing data
      Bluetooth: Implement LE L2CAP reassembly
      Bluetooth: Fix LE L2CAP Connect Request handling together with SMP
      Bluetooth: Fix suspending the L2CAP socket if we start with 0 credits
      Bluetooth: Limit LE MPS to the MTU value
      Bluetooth: Fix clearing of chan->omtu for LE CoC channels
      Bluetooth: Fix CID ranges for LE CoC CID allocations
      Bluetooth: Fix validating LE PSM values
      Bluetooth: Add debugfs controls for LE CoC MPS and Credits
      Bluetooth: Simplify l2cap_chan initialization for LE CoC
      Bluetooth: Use min_t for calculating chan->mps
      Bluetooth: Fix valid LE PSM check
      Bluetooth: Fix handling of L2CAP Command Reject over LE

Marcel Holtmann (2):
      Bluetooth: Increase minor version of core module
      Bluetooth: Use macros for connectionless slave broadcast features

 include/net/bluetooth/bluetooth.h |   3 +
 include/net/bluetooth/hci.h       |   6 +
 include/net/bluetooth/hci_core.h  |   6 +
 include/net/bluetooth/l2cap.h     |  45 +++
 net/bluetooth/af_bluetooth.c      |   2 +-
 net/bluetooth/hci_core.c          |   6 +-
 net/bluetooth/l2cap_core.c        | 744 ++++++++++++++++++++++++++++++++++++++++++++---
 net/bluetooth/l2cap_sock.c        | 181 ++++++++++--
 net/bluetooth/smp.c               |  16 +-
 net/bluetooth/smp.h               |   1 +
 10 files changed, 937 insertions(+), 73 deletions(-)

[-- Attachment #2: Type: application/pgp-signature, Size: 836 bytes --]

^ permalink raw reply

* [PATCH v8 5/5] Bluetooth: Manually enable or disable 6LoWPAN between devices
From: Jukka Rissanen @ 2013-12-10 15:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386689689-26550-1-git-send-email-jukka.rissanen@linux.intel.com>

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

Before connecting the devices do this

echo Y > /sys/kernel/debug/bluetooth/hci0/6lowpan

This enables 6LoWPAN support and creates the bt0 interface
automatically when devices are finally connected.

Rebooting or unloading the bluetooth kernel module will also clear the
settings from the kernel.

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

diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 8b8b5f8..924b87a 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -636,6 +636,92 @@ static int conn_max_interval_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
 			conn_max_interval_set, "%llu\n");
 
+static LIST_HEAD(lowpan_user_enabled);
+static DEFINE_RWLOCK(lowpan_enabled_lock);
+
+struct lowpan_enabled {
+	__u16 dev_id;
+	bool enabled;
+	struct list_head list;
+};
+
+static ssize_t lowpan_read(struct file *file, char __user *user_buf,
+			   size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[3];
+
+	buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer,
+			    size_t count, loff_t *position)
+{
+	struct hci_dev *hdev = fp->private_data;
+	struct lowpan_enabled *entry = NULL, *tmp;
+	bool new_value, old_value;
+	char buf[32];
+	size_t buf_size = min(count, (sizeof(buf)-1));
+	ssize_t ret;
+
+	BT_DBG("dev %s count %zd", hdev->dev_name, count);
+
+	if (copy_from_user(buf, user_buffer, buf_size))
+		return -EFAULT;
+
+	buf[buf_size] = '\0';
+
+	if (strtobool(buf, &new_value) < 0)
+		return -EINVAL;
+
+	ret = -ENOENT;
+
+	write_lock(&lowpan_enabled_lock);
+	list_for_each_entry_safe(entry, tmp, &lowpan_user_enabled, list) {
+		if (entry->dev_id == hdev->id) {
+			old_value = entry->enabled;
+			entry->enabled = new_value;
+			ret = 0;
+			break;
+		}
+	}
+	write_unlock(&lowpan_enabled_lock);
+
+	if (ret == 0 && old_value == new_value)
+		return count;
+
+	if (ret < 0) {
+		entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+		if (!entry)
+			return -ENOMEM;
+
+		entry->dev_id = hdev->id;
+		entry->enabled = new_value;
+
+		write_lock(&lowpan_enabled_lock);
+		INIT_LIST_HEAD(&entry->list);
+		list_add(&entry->list, &lowpan_user_enabled);
+		write_unlock(&lowpan_enabled_lock);
+	}
+
+	if (new_value == true)
+		set_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+	else
+		clear_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags);
+
+	return count;
+}
+
+static const struct file_operations lowpan_debugfs_fops = {
+	.open		= simple_open,
+	.read		= lowpan_read,
+	.write		= lowpan_write,
+	.llseek		= default_llseek,
+};
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -1406,6 +1492,8 @@ static int __hci_init(struct hci_dev *hdev)
 				    hdev, &conn_min_interval_fops);
 		debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
 				    hdev, &conn_max_interval_fops);
+		debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev,
+				    &lowpan_debugfs_fops);
 	}
 
 	return 0;
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v8 4/5] Bluetooth: Enable 6LoWPAN support for BT LE devices
From: Jukka Rissanen @ 2013-12-10 15:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386689689-26550-1-git-send-email-jukka.rissanen@linux.intel.com>

This is initial version of
http://tools.ietf.org/html/draft-ietf-6lo-btle-00

By default the 6LoWPAN support is not activated and user
needs to tweak /sys/kernel/debug/bluetooth/hci0/6lowpan
file.

The kernel needs IPv6 support before 6LoWPAN is usable.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
---
 include/net/bluetooth/hci.h      |   1 +
 include/net/bluetooth/hci_core.h |   1 +
 include/net/bluetooth/l2cap.h    |   1 +
 net/bluetooth/6lowpan.c          | 880 +++++++++++++++++++++++++++++++++++++++
 net/bluetooth/6lowpan.h          |  26 ++
 net/bluetooth/Makefile           |   6 +-
 net/bluetooth/hci_event.c        |   3 +
 net/bluetooth/l2cap_core.c       |  12 +
 8 files changed, 929 insertions(+), 1 deletion(-)
 create mode 100644 net/bluetooth/6lowpan.c
 create mode 100644 net/bluetooth/6lowpan.h

diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
index cc2da73..5dc3d90 100644
--- a/include/net/bluetooth/hci.h
+++ b/include/net/bluetooth/hci.h
@@ -131,6 +131,7 @@ enum {
 	HCI_PERIODIC_INQ,
 	HCI_FAST_CONNECTABLE,
 	HCI_BREDR_ENABLED,
+	HCI_6LOWPAN_ENABLED,
 };
 
 /* A mask for the flags that are supposed to remain when a reset happens
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index b796161..f2f0cf5 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -448,6 +448,7 @@ enum {
 	HCI_CONN_SSP_ENABLED,
 	HCI_CONN_POWER_SAVE,
 	HCI_CONN_REMOTE_OOB,
+	HCI_CONN_6LOWPAN,
 };
 
 static inline bool hci_conn_ssp_enabled(struct hci_conn *conn)
diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
index e149e99..dbc4a89 100644
--- a/include/net/bluetooth/l2cap.h
+++ b/include/net/bluetooth/l2cap.h
@@ -136,6 +136,7 @@ struct l2cap_conninfo {
 #define L2CAP_FC_L2CAP		0x02
 #define L2CAP_FC_CONNLESS	0x04
 #define L2CAP_FC_A2MP		0x08
+#define L2CAP_FC_6LOWPAN        0x3e /* reserved and temporary value */
 
 /* 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..5da0f99
--- /dev/null
+++ b/net/bluetooth/6lowpan.c
@@ -0,0 +1,880 @@
+/*
+   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/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> /* to get the address type */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/l2cap.h>
+
+#include "../ieee802154/6lowpan.h" /* for the compression support */
+
+#define IFACE_NAME_TEMPLATE "bt%d"
+#define EUI64_ADDR_LEN 8
+
+struct skb_cb {
+	struct in6_addr addr;
+	struct l2cap_conn *conn;
+};
+#define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb))
+
+/*
+ * 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 network (=hciX device).
+ * The list contains struct lowpan_dev elements.
+ */
+static LIST_HEAD(bt_6lowpan_devices);
+static DEFINE_RWLOCK(devices_lock);
+
+struct lowpan_dev {
+	struct net_device *dev;
+	struct work_struct delete_netdev;
+	struct list_head list;
+};
+
+struct lowpan_peer {
+	struct list_head list;
+	struct l2cap_conn *conn;
+
+	/* peer addresses in various formats */
+	unsigned char eui64_addr[EUI64_ADDR_LEN];
+	struct in6_addr peer_addr;
+};
+
+struct lowpan_info {
+	struct net_device *net;
+	struct list_head peers;
+	atomic_t peer_count; /* number of items in peers list */
+
+	struct delayed_work notify_peers;
+};
+
+static inline struct lowpan_info *lowpan_info(const struct net_device *dev)
+{
+	return netdev_priv(dev);
+}
+
+static inline void peer_add(struct lowpan_info *info, struct lowpan_peer *peer)
+{
+	list_add(&peer->list, &info->peers);
+	atomic_inc(&info->peer_count);
+}
+
+static inline bool peer_del(struct lowpan_info *info, struct lowpan_peer *peer)
+{
+	list_del(&peer->list);
+
+	if (atomic_dec_and_test(&info->peer_count)) {
+		BT_DBG("last peer");
+		return true;
+	}
+
+	return false;
+}
+
+static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_info *info,
+						 bdaddr_t *ba, __u8 type)
+{
+	struct lowpan_peer *peer, *tmp;
+
+	BT_DBG("peers %d addr %pMR type %d", atomic_read(&info->peer_count),
+					     ba, type);
+
+	list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+		BT_DBG("addr %pMR type %d",
+			&peer->conn->hcon->dst, peer->conn->hcon->dst_type);
+
+		if (!bacmp(&peer->conn->hcon->dst, ba))
+			return peer;
+	}
+
+	return NULL;
+}
+
+static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_info *info,
+						   struct l2cap_conn *conn)
+{
+	struct lowpan_peer *peer, *tmp;
+
+	list_for_each_entry_safe(peer, tmp, &info->peers, list) {
+		if (peer->conn == conn)
+			return peer;
+	}
+
+	return NULL;
+}
+
+static struct lowpan_peer *lookup_peer(struct l2cap_conn *conn,
+				       struct lowpan_info **dev)
+{
+	struct lowpan_dev *entry, *tmp;
+	struct lowpan_peer *peer = NULL;
+	unsigned long flags;
+
+	read_lock_irqsave(&devices_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		struct lowpan_info *info = lowpan_info(entry->dev);
+
+		peer = peer_lookup_conn(info, conn);
+		if (peer) {
+			if (dev)
+				*dev = info;
+			break;
+		}
+	}
+
+	read_unlock_irqrestore(&devices_lock, flags);
+
+	return peer;
+}
+
+/* print data in line */
+static inline void raw_dump_inline(const char *caller, char *msg,
+				   unsigned char *buf, int len)
+{
+	if (msg)
+		pr_debug("%s():%s: ", caller, msg);
+	print_hex_dump_debug("", DUMP_PREFIX_NONE,
+		       16, 1, buf, len, false);
+}
+
+/*
+ * 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)
+{
+	if (msg)
+		pr_debug("%s():%s:\n", caller, msg);
+	print_hex_dump_debug("\t", DUMP_PREFIX_OFFSET,
+		       16, 1, buf, len, false);
+}
+
+static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sk_buff *skb_cp;
+	int ret;
+
+	skb_cp = skb_copy(skb, GFP_ATOMIC);
+	if (!skb_cp)
+		return -ENOMEM;
+
+	ret = netif_rx(skb_cp);
+
+	BT_DBG("receive skb %d", ret);
+	if (ret < 0)
+		return NET_RX_DROP;
+
+	return ret;
+}
+
+static int process_data(struct sk_buff *skb, struct net_device *dev,
+			struct l2cap_conn *conn)
+{
+	const u8 *saddr, *daddr;
+	u8 iphc0, iphc1;
+	struct lowpan_info *info;
+	struct lowpan_peer *peer;
+	unsigned long flags;
+
+	info = lowpan_info(dev);
+
+	read_lock_irqsave(&devices_lock, flags);
+	peer = peer_lookup_conn(info, conn);
+	read_unlock_irqrestore(&devices_lock, flags);
+	if (!peer)
+		return -EINVAL;
+
+	saddr = peer->eui64_addr;
+	daddr = info->net->dev_addr;
+
+	/* at least two bytes will be used for the encoding */
+	if (skb->len < 2)
+		goto drop;
+
+	if (lowpan_fetch_skb_u8(skb, &iphc0))
+		goto drop;
+
+	if (lowpan_fetch_skb_u8(skb, &iphc1))
+		goto drop;
+
+	return lowpan_process_data(skb, dev,
+				saddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+				daddr, IEEE802154_ADDR_LONG, EUI64_ADDR_LEN,
+				iphc0, iphc1, give_skb_to_upper);
+
+drop:
+	kfree_skb(skb);
+	return -EINVAL;
+}
+
+static int recv_pkt(struct sk_buff *skb, struct net_device *dev,
+		    struct l2cap_conn *conn)
+{
+	struct sk_buff *local_skb;
+
+	if (!netif_running(dev))
+		goto drop;
+
+	if (dev->type != ARPHRD_6LOWPAN)
+		goto drop;
+
+	/* 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 */
+			local_skb = skb_clone(skb, GFP_ATOMIC);
+			if (!local_skb)
+				goto drop;
+			if (process_data(local_skb, dev, conn)
+							!= 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;
+}
+
+/* Packet from BT LE device */
+int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb)
+{
+	struct lowpan_info *info = NULL;
+	struct lowpan_peer *peer;
+	int err;
+
+	peer = lookup_peer(conn, &info);
+	if (!peer)
+		return -ENOENT;
+
+	if (!info->net)
+		return -ENOENT;
+
+	err = recv_pkt(skb, info->net, conn);
+	BT_DBG("recv pkt %d", err);
+
+	return err;
+}
+
+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);
+}
+
+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_ATOMIC);
+		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;
+
+	/*
+	 * XXX: This mtu check should be not needed and atm is only used for
+	 * testing purposes
+	 */
+	if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE))
+		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_ATOMIC);
+	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;
+
+	skb = create_pdu(conn, msg, len, priority, dev);
+	if (IS_ERR(skb))
+		return -EINVAL;
+
+	do_send(conn, skb);
+	return 0;
+}
+
+static void get_dest_bdaddr(struct in6_addr *ip6_daddr,
+			    bdaddr_t *addr, u8 *addr_type)
+{
+	u8 *eui64;
+
+	eui64 = ip6_daddr->s6_addr + 8;
+
+	addr->b[0] = eui64[7];
+	addr->b[1] = eui64[6];
+	addr->b[2] = eui64[5];
+	addr->b[3] = eui64[2];
+	addr->b[4] = eui64[1];
+	addr->b[5] = eui64[0];
+
+	addr->b[5] ^= 2;
+
+	/* Set universal/local bit to 0 */
+	if (addr->b[5] & 1) {
+		addr->b[5] &= ~1;
+		*addr_type = BDADDR_LE_PUBLIC;
+	} else
+		*addr_type = BDADDR_LE_RANDOM;
+}
+
+static int header_create(struct sk_buff *skb, struct net_device *dev,
+		         unsigned short type, const void *_daddr,
+		         const void *_saddr, unsigned int len)
+{
+	struct ipv6hdr *hdr;
+	struct lowpan_info *info;
+	struct lowpan_peer *peer;
+	bdaddr_t addr, *any = BDADDR_ANY;
+	u8 *saddr, *daddr = any->b;
+	u8 addr_type;
+
+	if (type != ETH_P_IPV6)
+		return -EINVAL;
+
+	hdr = ipv6_hdr(skb);
+
+	info = lowpan_info(dev);
+
+	if (ipv6_addr_is_multicast(&hdr->daddr)) {
+		memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+		       sizeof(struct in6_addr));
+		lowpan_cb(skb)->conn = NULL;
+	} else {
+		unsigned long flags;
+
+		/*
+		 * Get destination BT device from skb.
+		 * If there is no such peer then discard the packet.
+		 */
+		get_dest_bdaddr(&hdr->daddr, &addr, &addr_type);
+
+		BT_DBG("dest addr %pMR type %d", &addr, addr_type);
+
+		read_lock_irqsave(&devices_lock, flags);
+		peer = peer_lookup_ba(info, &addr, addr_type);
+		read_unlock_irqrestore(&devices_lock, flags);
+
+		if (!peer) {
+			BT_DBG("no such peer %pMR found", &addr);
+			return -ENOENT;
+		}
+
+		daddr = peer->eui64_addr;
+
+		memcpy(&lowpan_cb(skb)->addr, &hdr->daddr,
+						sizeof(struct in6_addr));
+		lowpan_cb(skb)->conn = peer->conn;
+	}
+
+	saddr = info->net->dev_addr;
+
+	return lowpan_header_compress(skb, dev, type, daddr, saddr, len);
+}
+
+/* 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 int send_mcast_pkt(struct sk_buff *skb, struct net_device *dev)
+{
+	struct sk_buff *local_skb;
+	struct lowpan_dev *entry, *tmp;
+	int err = 0;
+	unsigned long flags;
+
+	read_lock_irqsave(&devices_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		struct lowpan_peer *pentry, *ptmp;
+		struct lowpan_info *info;
+
+		if (entry->dev != dev)
+			continue;
+
+		info = lowpan_info(entry->dev);
+
+		list_for_each_entry_safe(pentry, ptmp, &info->peers, list) {
+			local_skb = skb_clone(skb, GFP_ATOMIC);
+
+			err = send_pkt(pentry->conn, dev->dev_addr,
+				       pentry->eui64_addr,
+				       local_skb, dev);
+
+			kfree_skb(local_skb);
+		}
+	}
+
+	read_unlock_irqrestore(&devices_lock, flags);
+
+	return err;
+}
+
+static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+	int err = -ENOENT;
+	unsigned char *eui64_addr;
+	struct lowpan_info *info;
+	struct lowpan_peer *peer;
+	bdaddr_t addr;
+	u8 addr_type;
+
+	if (ipv6_addr_is_multicast(&lowpan_cb(skb)->addr)) {
+		/*
+		 * We need to send the packet to every device
+		 * behind this interface.
+		 */
+		err = send_mcast_pkt(skb, dev);
+	} else {
+		unsigned long flags;
+
+		get_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type);
+		eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8;
+		info = lowpan_info(dev);
+
+		read_lock_irqsave(&devices_lock, flags);
+		peer = peer_lookup_ba(info, &addr, addr_type);
+		read_unlock_irqrestore(&devices_lock, flags);
+
+		BT_DBG("xmit from %s to %pMR (%pI6c), peer %p", dev->name,
+			&addr, &lowpan_cb(skb)->addr, peer);
+
+		if (peer && peer->conn)
+			err = send_pkt(peer->conn, dev->dev_addr, eui64_addr,
+					skb, dev);
+	}
+	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		= bt_xmit,
+};
+
+static struct header_ops header_ops = {
+	.create	= header_create,
+};
+
+static void netdev_setup(struct net_device *dev)
+{
+	dev->addr_len		= EUI64_ADDR_LEN;
+	dev->type		= ARPHRD_6LOWPAN;
+
+	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->header_ops		= &header_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;
+
+	/* Universal/local bit set, RFC 4291 */
+	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 err;
+
+	rtnl_lock();
+	err = dev_open(net);
+	if (err < 0)
+		BT_INFO("iface %s cannot be opened (%d)", net->name, err);
+	rtnl_unlock();
+}
+
+static void do_notify_peers(struct work_struct *work)
+{
+	struct lowpan_info *info = container_of(work, struct lowpan_info,
+						notify_peers.work);
+
+	netdev_notify_peers(info->net); /* send neighbour adv at startup */
+}
+
+static bool is_bt_6lowpan(struct hci_conn *hcon)
+{
+	if (hcon->type != LE_LINK)
+		return false;
+
+	return test_bit(HCI_CONN_6LOWPAN, &hcon->flags);
+}
+
+/*
+ * 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 lowpan_info *dev = NULL;
+	struct lowpan_peer *peer = NULL;
+	struct net_device *net;
+	struct lowpan_dev *entry;
+	int err = 0;
+	unsigned long flags;
+
+	if (!is_bt_6lowpan(conn->hcon))
+		return 0;
+
+	peer = lookup_peer(conn, &dev);
+	if (peer)
+		return -EEXIST;
+
+	if (dev && !peer)
+		goto add_peer;
+
+	net = alloc_netdev(sizeof(struct lowpan_info), IFACE_NAME_TEMPLATE,
+		           netdev_setup);
+	if (!net)
+		return -ENOMEM;
+
+	dev = netdev_priv(net);
+	dev->net = net;
+	INIT_LIST_HEAD(&dev->peers);
+
+	set_dev_addr(net, &conn->hcon->src, conn->hcon->src_type);
+
+	net->netdev_ops = &netdev_ops;
+	SET_NETDEV_DEV(net, &conn->hcon->dev);
+	SET_NETDEV_DEVTYPE(net, &bt_type);
+
+	err = register_netdev(net);
+	if (err < 0) {
+		BT_INFO("register_netdev failed %d", err);
+		free_netdev(net);
+		goto out;
+	}
+
+	BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR",
+		net->ifindex, &conn->hcon->dst, &conn->hcon->src);
+	set_bit(__LINK_STATE_PRESENT, &net->state);
+
+	entry = kzalloc(sizeof(struct lowpan_dev), GFP_ATOMIC);
+	if (!entry) {
+		unregister_netdev(net);
+		return -ENOMEM;
+	}
+
+	entry->dev = net;
+
+	write_lock_irqsave(&devices_lock, flags);
+	INIT_LIST_HEAD(&entry->list);
+	list_add(&entry->list, &bt_6lowpan_devices);
+	write_unlock_irqrestore(&devices_lock, flags);
+
+	ifup(net);
+
+add_peer:
+	peer = kzalloc(sizeof(struct lowpan_peer), GFP_ATOMIC);
+	if (!peer)
+		return -ENOMEM;
+
+	peer->conn = conn;
+	memset(&peer->peer_addr, 0, sizeof(struct in6_addr));
+
+	/* RFC 2464 ch. 5 */
+	peer->peer_addr.s6_addr[0] = 0xFE;
+	peer->peer_addr.s6_addr[1] = 0x80;
+	set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b,
+		conn->hcon->dst_type);
+
+	memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8,
+		EUI64_ADDR_LEN);
+	peer->eui64_addr[0] ^= 2; /* second bit-flip (Universe/Local)
+				   * is done according RFC2464
+				   */
+
+	raw_dump_inline(__func__, "peer IPv6 address",
+			(unsigned char *)&peer->peer_addr, 16);
+	raw_dump_inline(__func__, "peer EUI64 address", peer->eui64_addr, 8);
+
+	write_lock_irqsave(&devices_lock, flags);
+	INIT_LIST_HEAD(&peer->list);
+	peer_add(dev, peer);
+	write_unlock_irqrestore(&devices_lock, flags);
+
+	/*
+	 * Notifying peers about us needs to be done without locks held
+	 */
+	INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers);
+	schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100));
+
+out:
+	return err;
+}
+
+static void delete_netdev(struct work_struct *work)
+{
+	struct lowpan_dev *entry = container_of(work, struct lowpan_dev,
+						delete_netdev);
+
+	unregister_netdev(entry->dev);
+
+	/* The entry pointer is deleted in device_event() */
+}
+
+int bt_6lowpan_del_conn(struct l2cap_conn *conn)
+{
+	struct lowpan_dev *entry, *tmp;
+	struct lowpan_info *info = NULL;
+	struct lowpan_peer *peer;
+	int err = -ENOENT;
+	unsigned long flags;
+	bool last = false;
+
+	if (!is_bt_6lowpan(conn->hcon))
+		return 0;
+
+	write_lock_irqsave(&devices_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) {
+		info = lowpan_info(entry->dev);
+		peer = peer_lookup_conn(info, conn);
+		if (peer) {
+			last = peer_del(info, peer);
+			err = 0;
+			break;
+		}
+	}
+
+	if (!err && last && info && !atomic_read(&info->peer_count)) {
+		write_unlock_irqrestore(&devices_lock, flags);
+
+		cancel_delayed_work_sync(&info->notify_peers);
+
+		/*
+		 * bt_6lowpan_del_conn() is called with hci dev lock held which
+		 * means that we must delete the netdevice in worker thread.
+		 */
+		INIT_WORK(&entry->delete_netdev, delete_netdev);
+		schedule_work(&entry->delete_netdev);
+	} else
+		write_unlock_irqrestore(&devices_lock, flags);
+
+	return err;
+}
+
+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;
+	unsigned long flags;
+
+	if (dev->type != ARPHRD_6LOWPAN)
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case NETDEV_UNREGISTER:
+		write_lock_irqsave(&devices_lock, flags);
+		list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices,
+					list) {
+			if (entry->dev == dev) {
+				list_del(&entry->list);
+				kfree(entry);
+				break;
+			}
+		}
+		write_unlock_irqrestore(&devices_lock, flags);
+		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..cc6827e 100644
--- a/net/bluetooth/Makefile
+++ b/net/bluetooth/Makefile
@@ -10,6 +10,10 @@ 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
+
+ifeq ($(CONFIG_IEEE802154_6LOWPAN),)
+  bluetooth-y +=  ../ieee802154/6lowpan_iphc.o
+endif
 
 subdir-ccflags-y += -D__CHECK_ENDIAN__
diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
index 5fb3df6..5f81245 100644
--- a/net/bluetooth/hci_event.c
+++ b/net/bluetooth/hci_event.c
@@ -3533,6 +3533,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
 	conn->handle = __le16_to_cpu(ev->handle);
 	conn->state = BT_CONNECTED;
 
+	if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags))
+		set_bit(HCI_CONN_6LOWPAN, &conn->flags);
+
 	hci_conn_add_sysfs(conn);
 
 	hci_proto_connect_cfm(conn, ev->status);
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index ae0054c..e97248d 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;
 
@@ -1468,6 +1469,8 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn)
 
 	BT_DBG("");
 
+	bt_6lowpan_add_conn(conn);
+
 	/* Check if we have socket listening on cid */
 	pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT,
 					  &hcon->src, &hcon->dst);
@@ -7093,6 +7096,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;
@@ -7160,6 +7167,8 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
 {
 	BT_DBG("hcon %p reason %d", hcon, reason);
 
+	bt_6lowpan_del_conn(hcon->l2cap_data);
+
 	l2cap_conn_del(hcon, bt_to_errno(reason));
 }
 
@@ -7441,11 +7450,14 @@ int __init l2cap_init(void)
 	debugfs_create_u16("l2cap_le_default_mps", 0466, bt_debugfs,
 			   &le_default_mps);
 
+	bt_6lowpan_init();
+
 	return 0;
 }
 
 void l2cap_exit(void)
 {
+	bt_6lowpan_cleanup();
 	debugfs_remove(l2cap_debugfs);
 	l2cap_cleanup_sockets();
 }
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v8 3/5] ipv6: Add checks for 6LOWPAN ARP type
From: Jukka Rissanen @ 2013-12-10 15:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386689689-26550-1-git-send-email-jukka.rissanen@linux.intel.com>

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
---
 net/ipv6/addrconf.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c
index 12c97d8..d125fcd 100644
--- a/net/ipv6/addrconf.c
+++ b/net/ipv6/addrconf.c
@@ -1816,6 +1816,7 @@ static int ipv6_generate_eui64(u8 *eui, struct net_device *dev)
 		return addrconf_ifid_sit(eui, dev);
 	case ARPHRD_IPGRE:
 		return addrconf_ifid_gre(eui, dev);
+	case ARPHRD_6LOWPAN:
 	case ARPHRD_IEEE802154:
 		return addrconf_ifid_eui64(eui, dev);
 	case ARPHRD_IEEE1394:
@@ -2658,7 +2659,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_6LOWPAN)) {
 		/* Alas, we support only Ethernet autoconfiguration. */
 		return;
 	}
-- 
1.8.3.1


^ permalink raw reply related

* [PATCH v8 2/5] net: if_arp: add ARPHRD_6LOWPAN type
From: Jukka Rissanen @ 2013-12-10 15:34 UTC (permalink / raw)
  To: linux-bluetooth
In-Reply-To: <1386689689-26550-1-git-send-email-jukka.rissanen@linux.intel.com>

Used for IPv6 over LoWPAN networks. Example of this is
Bluetooth 6LoWPAN network.

Signed-off-by: Jukka Rissanen <jukka.rissanen@linux.intel.com>
Acked-by: David S. Miller <davem@davemloft.net>
---
 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..4d024d7 100644
--- a/include/uapi/linux/if_arp.h
+++ b/include/uapi/linux/if_arp.h
@@ -94,6 +94,7 @@
 #define ARPHRD_CAIF	822		/* CAIF media type		*/
 #define ARPHRD_IP6GRE	823		/* GRE over IPv6		*/
 #define ARPHRD_NETLINK	824		/* Netlink header		*/
+#define ARPHRD_6LOWPAN	825		/* IPv6 over LoWPAN             */
 
 #define ARPHRD_VOID	  0xFFFF	/* Void type, nothing is known */
 #define ARPHRD_NONE	  0xFFFE	/* zero header length */
-- 
1.8.3.1


^ 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