Browse Source

Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next

Johan Hedberg says:

====================
pull request: bluetooth-next 2015-10-08

Here's another set of Bluetooth & 802.15.4 patches for the 4.4 kernel.

802.15.4:
 - Many improvements & fixes to the mrf24j40 driver
 - Fixes and cleanups to nl802154, mac802154 & ieee802154 code

Bluetooth:
 - New chipset support in btmrvl driver
 - Fixes & cleanups to btbcm, btmrvl, bpa10x & btintel drivers
 - Support for vendor specific diagnostic data through common API
 - Cleanups to the 6lowpan code
 - New events & message types for monitor channel

Please let me know if there are any issues pulling. Thanks.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 10 years ago
parent
commit
7bcfeead48
60 changed files with 4079 additions and 1201 deletions
  1. 20 0
      Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
  2. 1 0
      MAINTAINERS
  3. 4 2
      drivers/bluetooth/Kconfig
  4. 0 11
      drivers/bluetooth/bfusb.c
  5. 1 7
      drivers/bluetooth/bluecard_cs.c
  6. 66 120
      drivers/bluetooth/bpa10x.c
  7. 1 7
      drivers/bluetooth/bt3c_cs.c
  8. 52 2
      drivers/bluetooth/btbcm.c
  9. 196 0
      drivers/bluetooth/btintel.c
  10. 9 0
      drivers/bluetooth/btintel.h
  11. 1 13
      drivers/bluetooth/btmrvl_main.c
  12. 48 6
      drivers/bluetooth/btmrvl_sdio.c
  13. 1 13
      drivers/bluetooth/btsdio.c
  14. 2 8
      drivers/bluetooth/btuart_cs.c
  15. 0 13
      drivers/bluetooth/btusb.c
  16. 0 13
      drivers/bluetooth/btwilink.c
  17. 0 5
      drivers/bluetooth/dtl1_cs.c
  18. 185 54
      drivers/bluetooth/hci_bcm.c
  19. 1 0
      drivers/bluetooth/hci_h4.c
  20. 5 5
      drivers/bluetooth/hci_h5.c
  21. 0 24
      drivers/bluetooth/hci_intel.c
  22. 3 14
      drivers/bluetooth/hci_ldisc.c
  23. 2 2
      drivers/bluetooth/hci_qca.c
  24. 0 1
      drivers/bluetooth/hci_uart.h
  25. 0 8
      drivers/bluetooth/hci_vhci.c
  26. 1 0
      drivers/net/ieee802154/Kconfig
  27. 89 122
      drivers/net/ieee802154/at86rf230.c
  28. 997 427
      drivers/net/ieee802154/mrf24j40.c
  29. 21 1
      include/linux/ieee802154.h
  30. 10 0
      include/net/6lowpan.h
  31. 5 0
      include/net/bluetooth/bluetooth.h
  32. 4 0
      include/net/bluetooth/hci.h
  33. 5 0
      include/net/bluetooth/hci_core.h
  34. 10 0
      include/net/bluetooth/hci_mon.h
  35. 164 0
      include/net/cfg802154.h
  36. 1 85
      include/net/ieee802154_netdev.h
  37. 1 9
      include/net/mac802154.h
  38. 18 0
      include/net/netlink.h
  39. 191 0
      include/net/nl802154.h
  40. 5 0
      net/6lowpan/core.c
  41. 0 2
      net/6lowpan/nhc.h
  42. 24 54
      net/bluetooth/6lowpan.c
  43. 128 14
      net/bluetooth/hci_core.c
  44. 62 8
      net/bluetooth/hci_sock.c
  45. 16 0
      net/bluetooth/lib.c
  46. 11 7
      net/ieee802154/6lowpan/core.c
  47. 2 0
      net/ieee802154/6lowpan/rx.c
  48. 35 14
      net/ieee802154/6lowpan/tx.c
  49. 5 0
      net/ieee802154/Kconfig
  50. 12 0
      net/ieee802154/core.c
  51. 1 0
      net/ieee802154/core.h
  52. 10 10
      net/ieee802154/header_ops.c
  53. 1224 97
      net/ieee802154/nl802154.c
  54. 109 0
      net/ieee802154/rdev-ops.h
  55. 4 4
      net/ieee802154/socket.c
  56. 205 0
      net/mac802154/cfg.c
  57. 102 16
      net/mac802154/iface.c
  58. 1 0
      net/mac802154/llsec.c
  59. 4 0
      net/mac802154/rx.c
  60. 4 3
      net/mac802154/tx.c

+ 20 - 0
Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt

@@ -0,0 +1,20 @@
+* MRF24J40 IEEE 802.15.4 *
+
+Required properties:
+  - compatible:		should be "microchip,mrf24j40", "microchip,mrf24j40ma",
+			or "microchip,mrf24j40mc" depends on your transceiver
+			board
+  - spi-max-frequency:	maximal bus speed, should be set something under or equal
+			10000000
+  - reg:		the chipselect index
+  - interrupts:		the interrupt generated by the device.
+
+Example:
+
+	mrf24j40ma@0 {
+		compatible = "microchip,mrf24j40ma";
+		spi-max-frequency = <8500000>;
+		reg = <0>;
+		interrupts = <19 8>;
+		interrupt-parent = <&gpio3>;
+	};

+ 1 - 0
MAINTAINERS

@@ -6978,6 +6978,7 @@ M:	Alan Ott <alan@signal11.us>
 L:	linux-wpan@vger.kernel.org
 L:	linux-wpan@vger.kernel.org
 S:	Maintained
 S:	Maintained
 F:	drivers/net/ieee802154/mrf24j40.c
 F:	drivers/net/ieee802154/mrf24j40.c
+F:	Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
 
 
 MSI LAPTOP SUPPORT
 MSI LAPTOP SUPPORT
 M:	"Lee, Chun-Yi" <jlee@suse.com>
 M:	"Lee, Chun-Yi" <jlee@suse.com>

+ 4 - 2
drivers/bluetooth/Kconfig

@@ -4,6 +4,7 @@ menu "Bluetooth device drivers"
 
 
 config BT_INTEL
 config BT_INTEL
 	tristate
 	tristate
+	select REGMAP
 
 
 config BT_BCM
 config BT_BCM
 	tristate
 	tristate
@@ -183,6 +184,7 @@ config BT_HCIBCM203X
 config BT_HCIBPA10X
 config BT_HCIBPA10X
 	tristate "HCI BPA10x USB driver"
 	tristate "HCI BPA10x USB driver"
 	depends on USB
 	depends on USB
+	select BT_HCIUART_H4
 	help
 	help
 	  Bluetooth HCI BPA10x USB driver.
 	  Bluetooth HCI BPA10x USB driver.
 	  This driver provides support for the Digianswer BPA 100/105 Bluetooth
 	  This driver provides support for the Digianswer BPA 100/105 Bluetooth
@@ -275,7 +277,7 @@ config BT_MRVL
 	  The core driver to support Marvell Bluetooth devices.
 	  The core driver to support Marvell Bluetooth devices.
 
 
 	  This driver is required if you want to support
 	  This driver is required if you want to support
-	  Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897.
+	  Marvell Bluetooth devices, such as 8688/8787/8797/8887/8897/8997.
 
 
 	  Say Y here to compile Marvell Bluetooth driver
 	  Say Y here to compile Marvell Bluetooth driver
 	  into the kernel or say M to compile it as module.
 	  into the kernel or say M to compile it as module.
@@ -289,7 +291,7 @@ config BT_MRVL_SDIO
 	  The driver for Marvell Bluetooth chipsets with SDIO interface.
 	  The driver for Marvell Bluetooth chipsets with SDIO interface.
 
 
 	  This driver is required if you want to use Marvell Bluetooth
 	  This driver is required if you want to use Marvell Bluetooth
-	  devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897
+	  devices with SDIO interface. Currently SD8688/SD8787/SD8797/SD8887/SD8897/SD8997
 	  chipsets are supported.
 	  chipsets are supported.
 
 
 	  Say Y here to compile support for Marvell BT-over-SDIO driver
 	  Say Y here to compile support for Marvell BT-over-SDIO driver

+ 0 - 11
drivers/bluetooth/bfusb.c

@@ -422,17 +422,12 @@ static int bfusb_open(struct hci_dev *hdev)
 
 
 	BT_DBG("hdev %p bfusb %p", hdev, data);
 	BT_DBG("hdev %p bfusb %p", hdev, data);
 
 
-	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	write_lock_irqsave(&data->lock, flags);
 	write_lock_irqsave(&data->lock, flags);
 
 
 	err = bfusb_rx_submit(data, NULL);
 	err = bfusb_rx_submit(data, NULL);
 	if (!err) {
 	if (!err) {
 		for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
 		for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
 			bfusb_rx_submit(data, NULL);
 			bfusb_rx_submit(data, NULL);
-	} else {
-		clear_bit(HCI_RUNNING, &hdev->flags);
 	}
 	}
 
 
 	write_unlock_irqrestore(&data->lock, flags);
 	write_unlock_irqrestore(&data->lock, flags);
@@ -458,9 +453,6 @@ static int bfusb_close(struct hci_dev *hdev)
 
 
 	BT_DBG("hdev %p bfusb %p", hdev, data);
 	BT_DBG("hdev %p bfusb %p", hdev, data);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	write_lock_irqsave(&data->lock, flags);
 	write_lock_irqsave(&data->lock, flags);
 	write_unlock_irqrestore(&data->lock, flags);
 	write_unlock_irqrestore(&data->lock, flags);
 
 
@@ -479,9 +471,6 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
 
 	BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
 	BT_DBG("hdev %p skb %p type %d len %d", hdev, skb, bt_cb(skb)->pkt_type, skb->len);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	switch (bt_cb(skb)->pkt_type) {
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
 	case HCI_COMMAND_PKT:
 		hdev->stat.cmd_tx++;
 		hdev->stat.cmd_tx++;

+ 1 - 7
drivers/bluetooth/bluecard_cs.c

@@ -390,7 +390,7 @@ static void bluecard_receive(struct bluecard_info *info,
 	for (i = 0; i < len; i++) {
 	for (i = 0; i < len; i++) {
 
 
 		/* Allocate packet */
 		/* Allocate packet */
-		if (info->rx_skb == NULL) {
+		if (!info->rx_skb) {
 			info->rx_state = RECV_WAIT_PACKET_TYPE;
 			info->rx_state = RECV_WAIT_PACKET_TYPE;
 			info->rx_count = 0;
 			info->rx_count = 0;
 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
@@ -628,9 +628,6 @@ static int bluecard_hci_open(struct hci_dev *hdev)
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state)))
 		bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
 		bluecard_hci_set_baud_rate(hdev, DEFAULT_BAUD_RATE);
 
 
-	if (test_and_set_bit(HCI_RUNNING, &(hdev->flags)))
-		return 0;
-
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
 		unsigned int iobase = info->p_dev->resource[0]->start;
 		unsigned int iobase = info->p_dev->resource[0]->start;
 
 
@@ -646,9 +643,6 @@ static int bluecard_hci_close(struct hci_dev *hdev)
 {
 {
 	struct bluecard_info *info = hci_get_drvdata(hdev);
 	struct bluecard_info *info = hci_get_drvdata(hdev);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-		return 0;
-
 	bluecard_hci_flush(hdev);
 	bluecard_hci_flush(hdev);
 
 
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
 	if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {

+ 66 - 120
drivers/bluetooth/bpa10x.c

@@ -35,7 +35,9 @@
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/hci_core.h>
 
 
-#define VERSION "0.10"
+#include "hci_uart.h"
+
+#define VERSION "0.11"
 
 
 static const struct usb_device_id bpa10x_table[] = {
 static const struct usb_device_id bpa10x_table[] = {
 	/* Tektronix BPA 100/105 (Digianswer) */
 	/* Tektronix BPA 100/105 (Digianswer) */
@@ -56,112 +58,6 @@ struct bpa10x_data {
 	struct sk_buff *rx_skb[2];
 	struct sk_buff *rx_skb[2];
 };
 };
 
 
-#define HCI_VENDOR_HDR_SIZE 5
-
-struct hci_vendor_hdr {
-	__u8    type;
-	__le16  snum;
-	__le16  dlen;
-} __packed;
-
-static int bpa10x_recv(struct hci_dev *hdev, int queue, void *buf, int count)
-{
-	struct bpa10x_data *data = hci_get_drvdata(hdev);
-
-	BT_DBG("%s queue %d buffer %p count %d", hdev->name,
-							queue, buf, count);
-
-	if (queue < 0 || queue > 1)
-		return -EILSEQ;
-
-	hdev->stat.byte_rx += count;
-
-	while (count) {
-		struct sk_buff *skb = data->rx_skb[queue];
-		struct { __u8 type; int expect; } *scb;
-		int type, len = 0;
-
-		if (!skb) {
-			/* Start of the frame */
-
-			type = *((__u8 *) buf);
-			count--; buf++;
-
-			switch (type) {
-			case HCI_EVENT_PKT:
-				if (count >= HCI_EVENT_HDR_SIZE) {
-					struct hci_event_hdr *h = buf;
-					len = HCI_EVENT_HDR_SIZE + h->plen;
-				} else
-					return -EILSEQ;
-				break;
-
-			case HCI_ACLDATA_PKT:
-				if (count >= HCI_ACL_HDR_SIZE) {
-					struct hci_acl_hdr *h = buf;
-					len = HCI_ACL_HDR_SIZE +
-							__le16_to_cpu(h->dlen);
-				} else
-					return -EILSEQ;
-				break;
-
-			case HCI_SCODATA_PKT:
-				if (count >= HCI_SCO_HDR_SIZE) {
-					struct hci_sco_hdr *h = buf;
-					len = HCI_SCO_HDR_SIZE + h->dlen;
-				} else
-					return -EILSEQ;
-				break;
-
-			case HCI_VENDOR_PKT:
-				if (count >= HCI_VENDOR_HDR_SIZE) {
-					struct hci_vendor_hdr *h = buf;
-					len = HCI_VENDOR_HDR_SIZE +
-							__le16_to_cpu(h->dlen);
-				} else
-					return -EILSEQ;
-				break;
-			}
-
-			skb = bt_skb_alloc(len, GFP_ATOMIC);
-			if (!skb) {
-				BT_ERR("%s no memory for packet", hdev->name);
-				return -ENOMEM;
-			}
-
-			data->rx_skb[queue] = skb;
-
-			scb = (void *) skb->cb;
-			scb->type = type;
-			scb->expect = len;
-		} else {
-			/* Continuation */
-
-			scb = (void *) skb->cb;
-			len = scb->expect;
-		}
-
-		len = min(len, count);
-
-		memcpy(skb_put(skb, len), buf, len);
-
-		scb->expect -= len;
-
-		if (scb->expect == 0) {
-			/* Complete frame */
-
-			data->rx_skb[queue] = NULL;
-
-			bt_cb(skb)->pkt_type = scb->type;
-			hci_recv_frame(hdev, skb);
-		}
-
-		count -= len; buf += len;
-	}
-
-	return 0;
-}
-
 static void bpa10x_tx_complete(struct urb *urb)
 static void bpa10x_tx_complete(struct urb *urb)
 {
 {
 	struct sk_buff *skb = urb->context;
 	struct sk_buff *skb = urb->context;
@@ -184,6 +80,22 @@ done:
 	kfree_skb(skb);
 	kfree_skb(skb);
 }
 }
 
 
+#define HCI_VENDOR_HDR_SIZE 5
+
+#define HCI_RECV_VENDOR \
+	.type = HCI_VENDOR_PKT, \
+	.hlen = HCI_VENDOR_HDR_SIZE, \
+	.loff = 3, \
+	.lsize = 2, \
+	.maxlen = HCI_MAX_FRAME_SIZE
+
+static const struct h4_recv_pkt bpa10x_recv_pkts[] = {
+	{ H4_RECV_ACL,     .recv = hci_recv_frame },
+	{ H4_RECV_SCO,     .recv = hci_recv_frame },
+	{ H4_RECV_EVENT,   .recv = hci_recv_frame },
+	{ HCI_RECV_VENDOR, .recv = hci_recv_diag  },
+};
+
 static void bpa10x_rx_complete(struct urb *urb)
 static void bpa10x_rx_complete(struct urb *urb)
 {
 {
 	struct hci_dev *hdev = urb->context;
 	struct hci_dev *hdev = urb->context;
@@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb)
 		return;
 		return;
 
 
 	if (urb->status == 0) {
 	if (urb->status == 0) {
-		if (bpa10x_recv(hdev, usb_pipebulk(urb->pipe),
+		bool idx = usb_pipebulk(urb->pipe);
+
+		data->rx_skb[idx] = h4_recv_buf(hdev, data->rx_skb[idx],
 						urb->transfer_buffer,
 						urb->transfer_buffer,
-						urb->actual_length) < 0) {
+						urb->actual_length,
+						bpa10x_recv_pkts,
+						ARRAY_SIZE(bpa10x_recv_pkts));
+		if (IS_ERR(data->rx_skb[idx])) {
 			BT_ERR("%s corrupted event packet", hdev->name);
 			BT_ERR("%s corrupted event packet", hdev->name);
 			hdev->stat.err_rx++;
 			hdev->stat.err_rx++;
+			data->rx_skb[idx] = NULL;
 		}
 		}
 	}
 	}
 
 
@@ -304,9 +222,6 @@ static int bpa10x_open(struct hci_dev *hdev)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	err = bpa10x_submit_intr_urb(hdev);
 	err = bpa10x_submit_intr_urb(hdev);
 	if (err < 0)
 	if (err < 0)
 		goto error;
 		goto error;
@@ -320,8 +235,6 @@ static int bpa10x_open(struct hci_dev *hdev)
 error:
 error:
 	usb_kill_anchored_urbs(&data->rx_anchor);
 	usb_kill_anchored_urbs(&data->rx_anchor);
 
 
-	clear_bit(HCI_RUNNING, &hdev->flags);
-
 	return err;
 	return err;
 }
 }
 
 
@@ -331,9 +244,6 @@ static int bpa10x_close(struct hci_dev *hdev)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	usb_kill_anchored_urbs(&data->rx_anchor);
 	usb_kill_anchored_urbs(&data->rx_anchor);
 
 
 	return 0;
 	return 0;
@@ -350,6 +260,24 @@ static int bpa10x_flush(struct hci_dev *hdev)
 	return 0;
 	return 0;
 }
 }
 
 
+static int bpa10x_setup(struct hci_dev *hdev)
+{
+	const u8 req[] = { 0x07 };
+	struct sk_buff *skb;
+
+	BT_DBG("%s", hdev->name);
+
+	/* Read revision string */
+	skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
+
+	kfree_skb(skb);
+	return 0;
+}
+
 static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
 {
 	struct bpa10x_data *data = hci_get_drvdata(hdev);
 	struct bpa10x_data *data = hci_get_drvdata(hdev);
@@ -360,9 +288,6 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	skb->dev = (void *) hdev;
 	skb->dev = (void *) hdev;
 
 
 	urb = usb_alloc_urb(0, GFP_ATOMIC);
 	urb = usb_alloc_urb(0, GFP_ATOMIC);
@@ -431,6 +356,25 @@ static int bpa10x_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 	return 0;
 	return 0;
 }
 }
 
 
+static int bpa10x_set_diag(struct hci_dev *hdev, bool enable)
+{
+	const u8 req[] = { 0x00, enable };
+	struct sk_buff *skb;
+
+	BT_DBG("%s", hdev->name);
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -ENETDOWN;
+
+	/* Enable sniffer operation */
+	skb = __hci_cmd_sync(hdev, 0xfc0e, sizeof(req), req, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+
 static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
 static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
 {
 	struct bpa10x_data *data;
 	struct bpa10x_data *data;
@@ -465,7 +409,9 @@ static int bpa10x_probe(struct usb_interface *intf, const struct usb_device_id *
 	hdev->open     = bpa10x_open;
 	hdev->open     = bpa10x_open;
 	hdev->close    = bpa10x_close;
 	hdev->close    = bpa10x_close;
 	hdev->flush    = bpa10x_flush;
 	hdev->flush    = bpa10x_flush;
+	hdev->setup    = bpa10x_setup;
 	hdev->send     = bpa10x_send_frame;
 	hdev->send     = bpa10x_send_frame;
+	hdev->set_diag = bpa10x_set_diag;
 
 
 	set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
 	set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
 
 

+ 1 - 7
drivers/bluetooth/bt3c_cs.c

@@ -233,7 +233,7 @@ static void bt3c_receive(struct bt3c_info *info)
 		info->hdev->stat.byte_rx++;
 		info->hdev->stat.byte_rx++;
 
 
 		/* Allocate packet */
 		/* Allocate packet */
-		if (info->rx_skb == NULL) {
+		if (!info->rx_skb) {
 			info->rx_state = RECV_WAIT_PACKET_TYPE;
 			info->rx_state = RECV_WAIT_PACKET_TYPE;
 			info->rx_count = 0;
 			info->rx_count = 0;
 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
@@ -270,7 +270,6 @@ static void bt3c_receive(struct bt3c_info *info)
 				/* Unknown packet */
 				/* Unknown packet */
 				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
 				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
 				info->hdev->stat.err_rx++;
 				info->hdev->stat.err_rx++;
-				clear_bit(HCI_RUNNING, &(info->hdev->flags));
 
 
 				kfree_skb(info->rx_skb);
 				kfree_skb(info->rx_skb);
 				info->rx_skb = NULL;
 				info->rx_skb = NULL;
@@ -395,17 +394,12 @@ static int bt3c_hci_flush(struct hci_dev *hdev)
 
 
 static int bt3c_hci_open(struct hci_dev *hdev)
 static int bt3c_hci_open(struct hci_dev *hdev)
 {
 {
-	set_bit(HCI_RUNNING, &(hdev->flags));
-
 	return 0;
 	return 0;
 }
 }
 
 
 
 
 static int bt3c_hci_close(struct hci_dev *hdev)
 static int bt3c_hci_close(struct hci_dev *hdev)
 {
 {
-	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-		return 0;
-
 	bt3c_hci_flush(hdev);
 	bt3c_hci_flush(hdev);
 
 
 	return 0;
 	return 0;

+ 52 - 2
drivers/bluetooth/btbcm.c

@@ -181,6 +181,27 @@ static int btbcm_reset(struct hci_dev *hdev)
 	return 0;
 	return 0;
 }
 }
 
 
+static struct sk_buff *btbcm_read_local_name(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_NAME, 0, NULL,
+			     HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb)) {
+		BT_ERR("%s: BCM: Reading local name failed (%ld)",
+		       hdev->name, PTR_ERR(skb));
+		return skb;
+	}
+
+	if (skb->len != sizeof(struct hci_rp_read_local_name)) {
+		BT_ERR("%s: BCM: Local name length mismatch", hdev->name);
+		kfree_skb(skb);
+		return ERR_PTR(-EIO);
+	}
+
+	return skb;
+}
+
 static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
 static struct sk_buff *btbcm_read_local_version(struct hci_dev *hdev)
 {
 {
 	struct sk_buff *skb;
 	struct sk_buff *skb;
@@ -393,6 +414,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
 	BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
 	BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
 	kfree_skb(skb);
 	kfree_skb(skb);
 
 
+	/* Read Local Name */
+	skb = btbcm_read_local_name(hdev);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
+	kfree_skb(skb);
+
 	switch ((rev & 0xf000) >> 12) {
 	switch ((rev & 0xf000) >> 12) {
 	case 0:
 	case 0:
 	case 3:
 	case 3:
@@ -464,6 +493,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
 		hw_name ? : "BCM", (subver & 0x7000) >> 13,
 		hw_name ? : "BCM", (subver & 0x7000) >> 13,
 		(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
 		(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
 
 
+	/* Read Local Name */
+	skb = btbcm_read_local_name(hdev);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
+	kfree_skb(skb);
+
 	btbcm_check_bdaddr(hdev);
 	btbcm_check_bdaddr(hdev);
 
 
 	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
 	set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
@@ -475,12 +512,25 @@ EXPORT_SYMBOL_GPL(btbcm_setup_patchram);
 int btbcm_setup_apple(struct hci_dev *hdev)
 int btbcm_setup_apple(struct hci_dev *hdev)
 {
 {
 	struct sk_buff *skb;
 	struct sk_buff *skb;
+	int err;
+
+	/* Reset */
+	err = btbcm_reset(hdev);
+	if (err)
+		return err;
 
 
 	/* Read Verbose Config Version Info */
 	/* Read Verbose Config Version Info */
 	skb = btbcm_read_verbose_config(hdev);
 	skb = btbcm_read_verbose_config(hdev);
 	if (!IS_ERR(skb)) {
 	if (!IS_ERR(skb)) {
-		BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name, skb->data[1],
-			get_unaligned_le16(skb->data + 5));
+		BT_INFO("%s: BCM: chip id %u build %4.4u", hdev->name,
+			skb->data[1], get_unaligned_le16(skb->data + 5));
+		kfree_skb(skb);
+	}
+
+	/* Read Local Name */
+	skb = btbcm_read_local_name(hdev);
+	if (!IS_ERR(skb)) {
+		BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
 		kfree_skb(skb);
 		kfree_skb(skb);
 	}
 	}
 
 

+ 196 - 0
drivers/bluetooth/btintel.c

@@ -23,6 +23,7 @@
 
 
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/firmware.h>
 #include <linux/firmware.h>
+#include <linux/regmap.h>
 
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/hci_core.h>
@@ -215,6 +216,201 @@ int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name)
 }
 }
 EXPORT_SYMBOL_GPL(btintel_load_ddc_config);
 EXPORT_SYMBOL_GPL(btintel_load_ddc_config);
 
 
+/* ------- REGMAP IBT SUPPORT ------- */
+
+#define IBT_REG_MODE_8BIT  0x00
+#define IBT_REG_MODE_16BIT 0x01
+#define IBT_REG_MODE_32BIT 0x02
+
+struct regmap_ibt_context {
+	struct hci_dev *hdev;
+	__u16 op_write;
+	__u16 op_read;
+};
+
+struct ibt_cp_reg_access {
+	__le32  addr;
+	__u8    mode;
+	__u8    len;
+	__u8    data[0];
+} __packed;
+
+struct ibt_rp_reg_access {
+	__u8    status;
+	__le32  addr;
+	__u8    data[0];
+} __packed;
+
+static int regmap_ibt_read(void *context, const void *addr, size_t reg_size,
+			   void *val, size_t val_size)
+{
+	struct regmap_ibt_context *ctx = context;
+	struct ibt_cp_reg_access cp;
+	struct ibt_rp_reg_access *rp;
+	struct sk_buff *skb;
+	int err = 0;
+
+	if (reg_size != sizeof(__le32))
+		return -EINVAL;
+
+	switch (val_size) {
+	case 1:
+		cp.mode = IBT_REG_MODE_8BIT;
+		break;
+	case 2:
+		cp.mode = IBT_REG_MODE_16BIT;
+		break;
+	case 4:
+		cp.mode = IBT_REG_MODE_32BIT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* regmap provides a little-endian formatted addr */
+	cp.addr = *(__le32 *)addr;
+	cp.len = val_size;
+
+	bt_dev_dbg(ctx->hdev, "Register (0x%x) read", le32_to_cpu(cp.addr));
+
+	skb = hci_cmd_sync(ctx->hdev, ctx->op_read, sizeof(cp), &cp,
+			   HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error (%d)",
+			   le32_to_cpu(cp.addr), err);
+		return err;
+	}
+
+	if (skb->len != sizeof(*rp) + val_size) {
+		bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad len",
+			   le32_to_cpu(cp.addr));
+		err = -EINVAL;
+		goto done;
+	}
+
+	rp = (struct ibt_rp_reg_access *)skb->data;
+
+	if (rp->addr != cp.addr) {
+		bt_dev_err(ctx->hdev, "regmap: Register (0x%x) read error, bad addr",
+			   le32_to_cpu(rp->addr));
+		err = -EINVAL;
+		goto done;
+	}
+
+	memcpy(val, rp->data, val_size);
+
+done:
+	kfree_skb(skb);
+	return err;
+}
+
+static int regmap_ibt_gather_write(void *context,
+				   const void *addr, size_t reg_size,
+				   const void *val, size_t val_size)
+{
+	struct regmap_ibt_context *ctx = context;
+	struct ibt_cp_reg_access *cp;
+	struct sk_buff *skb;
+	int plen = sizeof(*cp) + val_size;
+	u8 mode;
+	int err = 0;
+
+	if (reg_size != sizeof(__le32))
+		return -EINVAL;
+
+	switch (val_size) {
+	case 1:
+		mode = IBT_REG_MODE_8BIT;
+		break;
+	case 2:
+		mode = IBT_REG_MODE_16BIT;
+		break;
+	case 4:
+		mode = IBT_REG_MODE_32BIT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	cp = kmalloc(plen, GFP_KERNEL);
+	if (!cp)
+		return -ENOMEM;
+
+	/* regmap provides a little-endian formatted addr/value */
+	cp->addr = *(__le32 *)addr;
+	cp->mode = mode;
+	cp->len = val_size;
+	memcpy(&cp->data, val, val_size);
+
+	bt_dev_dbg(ctx->hdev, "Register (0x%x) write", le32_to_cpu(cp->addr));
+
+	skb = hci_cmd_sync(ctx->hdev, ctx->op_write, plen, cp, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb)) {
+		err = PTR_ERR(skb);
+		bt_dev_err(ctx->hdev, "regmap: Register (0x%x) write error (%d)",
+			   le32_to_cpu(cp->addr), err);
+		goto done;
+	}
+	kfree_skb(skb);
+
+done:
+	kfree(cp);
+	return err;
+}
+
+static int regmap_ibt_write(void *context, const void *data, size_t count)
+{
+	/* data contains register+value, since we only support 32bit addr,
+	 * minimum data size is 4 bytes.
+	 */
+	if (WARN_ONCE(count < 4, "Invalid register access"))
+		return -EINVAL;
+
+	return regmap_ibt_gather_write(context, data, 4, data + 4, count - 4);
+}
+
+static void regmap_ibt_free_context(void *context)
+{
+	kfree(context);
+}
+
+static struct regmap_bus regmap_ibt = {
+	.read = regmap_ibt_read,
+	.write = regmap_ibt_write,
+	.gather_write = regmap_ibt_gather_write,
+	.free_context = regmap_ibt_free_context,
+	.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+	.val_format_endian_default = REGMAP_ENDIAN_LITTLE,
+};
+
+/* Config is the same for all register regions */
+static const struct regmap_config regmap_ibt_cfg = {
+	.name      = "btintel_regmap",
+	.reg_bits  = 32,
+	.val_bits  = 32,
+};
+
+struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
+				   u16 opcode_write)
+{
+	struct regmap_ibt_context *ctx;
+
+	bt_dev_info(hdev, "regmap: Init R%x-W%x region", opcode_read,
+		    opcode_write);
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return ERR_PTR(-ENOMEM);
+
+	ctx->op_read = opcode_read;
+	ctx->op_write = opcode_write;
+	ctx->hdev = hdev;
+
+	return regmap_init(&hdev->dev, &regmap_ibt, ctx, &regmap_ibt_cfg);
+}
+EXPORT_SYMBOL_GPL(btintel_regmap_init);
+
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
 MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
 MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
 MODULE_VERSION(VERSION);
 MODULE_VERSION(VERSION);

+ 9 - 0
drivers/bluetooth/btintel.h

@@ -80,6 +80,9 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
 			const void *param);
 			const void *param);
 int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
 int btintel_load_ddc_config(struct hci_dev *hdev, const char *ddc_name);
 
 
+struct regmap *btintel_regmap_init(struct hci_dev *hdev, u16 opcode_read,
+				   u16 opcode_write);
+
 #else
 #else
 
 
 static inline int btintel_check_bdaddr(struct hci_dev *hdev)
 static inline int btintel_check_bdaddr(struct hci_dev *hdev)
@@ -113,4 +116,10 @@ static inline int btintel_load_ddc_config(struct hci_dev *hdev,
 	return -EOPNOTSUPP;
 	return -EOPNOTSUPP;
 }
 }
 
 
+static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
+						 u16 opcode_read,
+						 u16 opcode_write)
+{
+	return ERR_PTR(-EINVAL);
+}
 #endif
 #endif

+ 1 - 13
drivers/bluetooth/btmrvl_main.c

@@ -184,7 +184,7 @@ static int btmrvl_send_sync_cmd(struct btmrvl_private *priv, u16 opcode,
 	}
 	}
 
 
 	skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
 	skb = bt_skb_alloc(HCI_COMMAND_HDR_SIZE + len, GFP_ATOMIC);
-	if (skb == NULL) {
+	if (!skb) {
 		BT_ERR("No free skb");
 		BT_ERR("No free skb");
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
@@ -436,13 +436,6 @@ static int btmrvl_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
 
 	BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
 	BT_DBG("type=%d, len=%d", skb->pkt_type, skb->len);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
-		BT_ERR("Failed testing HCI_RUNING, flags=%lx", hdev->flags);
-		print_hex_dump_bytes("data: ", DUMP_PREFIX_OFFSET,
-							skb->data, skb->len);
-		return -EBUSY;
-	}
-
 	switch (bt_cb(skb)->pkt_type) {
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
 	case HCI_COMMAND_PKT:
 		hdev->stat.cmd_tx++;
 		hdev->stat.cmd_tx++;
@@ -477,9 +470,6 @@ static int btmrvl_close(struct hci_dev *hdev)
 {
 {
 	struct btmrvl_private *priv = hci_get_drvdata(hdev);
 	struct btmrvl_private *priv = hci_get_drvdata(hdev);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	skb_queue_purge(&priv->adapter->tx_queue);
 	skb_queue_purge(&priv->adapter->tx_queue);
 
 
 	return 0;
 	return 0;
@@ -487,8 +477,6 @@ static int btmrvl_close(struct hci_dev *hdev)
 
 
 static int btmrvl_open(struct hci_dev *hdev)
 static int btmrvl_open(struct hci_dev *hdev)
 {
 {
-	set_bit(HCI_RUNNING, &hdev->flags);
-
 	return 0;
 	return 0;
 }
 }
 
 

+ 48 - 6
drivers/bluetooth/btmrvl_sdio.c

@@ -146,6 +146,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
 	.fw_dump_end = 0xea,
 	.fw_dump_end = 0xea,
 };
 };
 
 
+static const struct btmrvl_sdio_card_reg btmrvl_reg_8997 = {
+	.cfg = 0x00,
+	.host_int_mask = 0x08,
+	.host_intstatus = 0x0c,
+	.card_status = 0x5c,
+	.sq_read_base_addr_a0 = 0xf8,
+	.sq_read_base_addr_a1 = 0xf9,
+	.card_revision = 0xc8,
+	.card_fw_status0 = 0xe8,
+	.card_fw_status1 = 0xe9,
+	.card_rx_len = 0xea,
+	.card_rx_unit = 0xeb,
+	.io_port_0 = 0xe4,
+	.io_port_1 = 0xe5,
+	.io_port_2 = 0xe6,
+	.int_read_to_clear = true,
+	.host_int_rsr = 0x04,
+	.card_misc_cfg = 0xD8,
+	.fw_dump_ctrl = 0xf0,
+	.fw_dump_start = 0xf1,
+	.fw_dump_end = 0xf8,
+};
+
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
 static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = {
 	.helper		= "mrvl/sd8688_helper.bin",
 	.helper		= "mrvl/sd8688_helper.bin",
 	.firmware	= "mrvl/sd8688.bin",
 	.firmware	= "mrvl/sd8688.bin",
@@ -191,25 +214,37 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
 	.supports_fw_dump = true,
 	.supports_fw_dump = true,
 };
 };
 
 
+static const struct btmrvl_sdio_device btmrvl_sdio_sd8997 = {
+	.helper         = NULL,
+	.firmware       = "mrvl/sd8997_uapsta.bin",
+	.reg            = &btmrvl_reg_8997,
+	.support_pscan_win_report = true,
+	.sd_blksz_fw_dl = 256,
+	.supports_fw_dump = true,
+};
+
 static const struct sdio_device_id btmrvl_sdio_ids[] = {
 static const struct sdio_device_id btmrvl_sdio_ids[] = {
 	/* Marvell SD8688 Bluetooth device */
 	/* Marvell SD8688 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9105),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8688 },
+			.driver_data = (unsigned long)&btmrvl_sdio_sd8688 },
 	/* Marvell SD8787 Bluetooth device */
 	/* Marvell SD8787 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911A),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+			.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
 	/* Marvell SD8787 Bluetooth AMP device */
 	/* Marvell SD8787 Bluetooth AMP device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x911B),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8787 },
+			.driver_data = (unsigned long)&btmrvl_sdio_sd8787 },
 	/* Marvell SD8797 Bluetooth device */
 	/* Marvell SD8797 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912A),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8797 },
+			.driver_data = (unsigned long)&btmrvl_sdio_sd8797 },
 	/* Marvell SD8887 Bluetooth device */
 	/* Marvell SD8887 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
 			.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
 			.driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
 	/* Marvell SD8897 Bluetooth device */
 	/* Marvell SD8897 Bluetooth device */
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
 	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x912E),
-			.driver_data = (unsigned long) &btmrvl_sdio_sd8897 },
+			.driver_data = (unsigned long)&btmrvl_sdio_sd8897 },
+	/* Marvell SD8997 Bluetooth device */
+	{ SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9142),
+			.driver_data = (unsigned long)&btmrvl_sdio_sd8997 },
 
 
 	{ }	/* Terminating entry */
 	{ }	/* Terminating entry */
 };
 };
@@ -619,7 +654,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
 
 
 	/* Allocate buffer */
 	/* Allocate buffer */
 	skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
 	skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
-	if (skb == NULL) {
+	if (!skb) {
 		BT_ERR("No free skb");
 		BT_ERR("No free skb");
 		ret = -ENOMEM;
 		ret = -ENOMEM;
 		goto exit;
 		goto exit;
@@ -1278,6 +1313,12 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
 
 
 		if (memory_size == 0) {
 		if (memory_size == 0) {
 			BT_INFO("Firmware dump finished!");
 			BT_INFO("Firmware dump finished!");
+			sdio_writeb(card->func, FW_DUMP_READ_DONE,
+				    card->reg->fw_dump_ctrl, &ret);
+			if (ret) {
+				BT_ERR("SDIO Write MEMDUMP_FINISH ERR");
+				goto done;
+			}
 			break;
 			break;
 		}
 		}
 
 
@@ -1616,3 +1657,4 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin");

+ 1 - 13
drivers/bluetooth/btsdio.c

@@ -194,21 +194,15 @@ static int btsdio_open(struct hci_dev *hdev)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	sdio_claim_host(data->func);
 	sdio_claim_host(data->func);
 
 
 	err = sdio_enable_func(data->func);
 	err = sdio_enable_func(data->func);
-	if (err < 0) {
-		clear_bit(HCI_RUNNING, &hdev->flags);
+	if (err < 0)
 		goto release;
 		goto release;
-	}
 
 
 	err = sdio_claim_irq(data->func, btsdio_interrupt);
 	err = sdio_claim_irq(data->func, btsdio_interrupt);
 	if (err < 0) {
 	if (err < 0) {
 		sdio_disable_func(data->func);
 		sdio_disable_func(data->func);
-		clear_bit(HCI_RUNNING, &hdev->flags);
 		goto release;
 		goto release;
 	}
 	}
 
 
@@ -229,9 +223,6 @@ static int btsdio_close(struct hci_dev *hdev)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	sdio_claim_host(data->func);
 	sdio_claim_host(data->func);
 
 
 	sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
 	sdio_writeb(data->func, 0x00, REG_EN_INTRD, NULL);
@@ -261,9 +252,6 @@ static int btsdio_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	switch (bt_cb(skb)->pkt_type) {
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
 	case HCI_COMMAND_PKT:
 		hdev->stat.cmd_tx++;
 		hdev->stat.cmd_tx++;

+ 2 - 8
drivers/bluetooth/btuart_cs.c

@@ -38,7 +38,7 @@
 #include <linux/serial.h>
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
 #include <linux/serial_reg.h>
 #include <linux/bitops.h>
 #include <linux/bitops.h>
-#include <asm/io.h>
+#include <linux/io.h>
 
 
 #include <pcmcia/cistpl.h>
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ciscode.h>
 #include <pcmcia/ciscode.h>
@@ -188,7 +188,7 @@ static void btuart_receive(struct btuart_info *info)
 		info->hdev->stat.byte_rx++;
 		info->hdev->stat.byte_rx++;
 
 
 		/* Allocate packet */
 		/* Allocate packet */
-		if (info->rx_skb == NULL) {
+		if (!info->rx_skb) {
 			info->rx_state = RECV_WAIT_PACKET_TYPE;
 			info->rx_state = RECV_WAIT_PACKET_TYPE;
 			info->rx_count = 0;
 			info->rx_count = 0;
 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
 			info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC);
@@ -223,7 +223,6 @@ static void btuart_receive(struct btuart_info *info)
 				/* Unknown packet */
 				/* Unknown packet */
 				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
 				BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
 				info->hdev->stat.err_rx++;
 				info->hdev->stat.err_rx++;
-				clear_bit(HCI_RUNNING, &(info->hdev->flags));
 
 
 				kfree_skb(info->rx_skb);
 				kfree_skb(info->rx_skb);
 				info->rx_skb = NULL;
 				info->rx_skb = NULL;
@@ -409,17 +408,12 @@ static int btuart_hci_flush(struct hci_dev *hdev)
 
 
 static int btuart_hci_open(struct hci_dev *hdev)
 static int btuart_hci_open(struct hci_dev *hdev)
 {
 {
-	set_bit(HCI_RUNNING, &(hdev->flags));
-
 	return 0;
 	return 0;
 }
 }
 
 
 
 
 static int btuart_hci_close(struct hci_dev *hdev)
 static int btuart_hci_close(struct hci_dev *hdev)
 {
 {
-	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-		return 0;
-
 	btuart_hci_flush(hdev);
 	btuart_hci_flush(hdev);
 
 
 	return 0;
 	return 0;

+ 0 - 13
drivers/bluetooth/btusb.c

@@ -940,9 +940,6 @@ static int btusb_open(struct hci_dev *hdev)
 
 
 	data->intf->needs_remote_wakeup = 1;
 	data->intf->needs_remote_wakeup = 1;
 
 
-	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		goto done;
-
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 	if (test_and_set_bit(BTUSB_INTR_RUNNING, &data->flags))
 		goto done;
 		goto done;
 
 
@@ -965,7 +962,6 @@ done:
 
 
 failed:
 failed:
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
 	clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-	clear_bit(HCI_RUNNING, &hdev->flags);
 	usb_autopm_put_interface(data->intf);
 	usb_autopm_put_interface(data->intf);
 	return err;
 	return err;
 }
 }
@@ -984,9 +980,6 @@ static int btusb_close(struct hci_dev *hdev)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	cancel_work_sync(&data->work);
 	cancel_work_sync(&data->work);
 	cancel_work_sync(&data->waker);
 	cancel_work_sync(&data->waker);
 
 
@@ -1156,9 +1149,6 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	switch (bt_cb(skb)->pkt_type) {
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
 	case HCI_COMMAND_PKT:
 		urb = alloc_ctrl_urb(hdev, skb);
 		urb = alloc_ctrl_urb(hdev, skb);
@@ -1843,9 +1833,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
 
 
 	BT_DBG("%s", hdev->name);
 	BT_DBG("%s", hdev->name);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	switch (bt_cb(skb)->pkt_type) {
 	switch (bt_cb(skb)->pkt_type) {
 	case HCI_COMMAND_PKT:
 	case HCI_COMMAND_PKT:
 		if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
 		if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {

+ 0 - 13
drivers/bluetooth/btwilink.c

@@ -155,9 +155,6 @@ static int ti_st_open(struct hci_dev *hdev)
 
 
 	BT_DBG("%s %p", hdev->name, hdev);
 	BT_DBG("%s %p", hdev->name, hdev);
 
 
-	if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	/* provide contexts for callbacks from ST */
 	/* provide contexts for callbacks from ST */
 	hst = hci_get_drvdata(hdev);
 	hst = hci_get_drvdata(hdev);
 
 
@@ -181,7 +178,6 @@ static int ti_st_open(struct hci_dev *hdev)
 			goto done;
 			goto done;
 
 
 		if (err != -EINPROGRESS) {
 		if (err != -EINPROGRESS) {
-			clear_bit(HCI_RUNNING, &hdev->flags);
 			BT_ERR("st_register failed %d", err);
 			BT_ERR("st_register failed %d", err);
 			return err;
 			return err;
 		}
 		}
@@ -195,7 +191,6 @@ static int ti_st_open(struct hci_dev *hdev)
 			(&hst->wait_reg_completion,
 			(&hst->wait_reg_completion,
 			 msecs_to_jiffies(BT_REGISTER_TIMEOUT));
 			 msecs_to_jiffies(BT_REGISTER_TIMEOUT));
 		if (!timeleft) {
 		if (!timeleft) {
-			clear_bit(HCI_RUNNING, &hdev->flags);
 			BT_ERR("Timeout(%d sec),didn't get reg "
 			BT_ERR("Timeout(%d sec),didn't get reg "
 					"completion signal from ST",
 					"completion signal from ST",
 					BT_REGISTER_TIMEOUT / 1000);
 					BT_REGISTER_TIMEOUT / 1000);
@@ -205,7 +200,6 @@ static int ti_st_open(struct hci_dev *hdev)
 		/* Is ST registration callback
 		/* Is ST registration callback
 		 * called with ERROR status? */
 		 * called with ERROR status? */
 		if (hst->reg_status != 0) {
 		if (hst->reg_status != 0) {
-			clear_bit(HCI_RUNNING, &hdev->flags);
 			BT_ERR("ST registration completed with invalid "
 			BT_ERR("ST registration completed with invalid "
 					"status %d", hst->reg_status);
 					"status %d", hst->reg_status);
 			return -EAGAIN;
 			return -EAGAIN;
@@ -215,7 +209,6 @@ done:
 		hst->st_write = ti_st_proto[i].write;
 		hst->st_write = ti_st_proto[i].write;
 		if (!hst->st_write) {
 		if (!hst->st_write) {
 			BT_ERR("undefined ST write function");
 			BT_ERR("undefined ST write function");
-			clear_bit(HCI_RUNNING, &hdev->flags);
 			for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
 			for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
 				/* Undo registration with ST */
 				/* Undo registration with ST */
 				err = st_unregister(&ti_st_proto[i]);
 				err = st_unregister(&ti_st_proto[i]);
@@ -236,9 +229,6 @@ static int ti_st_close(struct hci_dev *hdev)
 	int err, i;
 	int err, i;
 	struct ti_st *hst = hci_get_drvdata(hdev);
 	struct ti_st *hst = hci_get_drvdata(hdev);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
 	for (i = MAX_BT_CHNL_IDS-1; i >= 0; i--) {
 		err = st_unregister(&ti_st_proto[i]);
 		err = st_unregister(&ti_st_proto[i]);
 		if (err)
 		if (err)
@@ -256,9 +246,6 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 	struct ti_st *hst;
 	struct ti_st *hst;
 	long len;
 	long len;
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	hst = hci_get_drvdata(hdev);
 	hst = hci_get_drvdata(hdev);
 
 
 	/* Prepend skb with frame type */
 	/* Prepend skb with frame type */

+ 0 - 5
drivers/bluetooth/dtl1_cs.c

@@ -357,8 +357,6 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
 
 
 static int dtl1_hci_open(struct hci_dev *hdev)
 static int dtl1_hci_open(struct hci_dev *hdev)
 {
 {
-	set_bit(HCI_RUNNING, &(hdev->flags));
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -376,9 +374,6 @@ static int dtl1_hci_flush(struct hci_dev *hdev)
 
 
 static int dtl1_hci_close(struct hci_dev *hdev)
 static int dtl1_hci_close(struct hci_dev *hdev)
 {
 {
-	if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-		return 0;
-
 	dtl1_hci_flush(hdev);
 	dtl1_hci_flush(hdev);
 
 
 	return 0;
 	return 0;

+ 185 - 54
drivers/bluetooth/hci_bcm.c

@@ -32,6 +32,8 @@
 #include <linux/gpio/consumer.h>
 #include <linux/gpio/consumer.h>
 #include <linux/tty.h>
 #include <linux/tty.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
+#include <linux/dmi.h>
+#include <linux/pm_runtime.h>
 
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include <net/bluetooth/hci_core.h>
@@ -39,6 +41,11 @@
 #include "btbcm.h"
 #include "btbcm.h"
 #include "hci_uart.h"
 #include "hci_uart.h"
 
 
+#define BCM_LM_DIAG_PKT 0x07
+#define BCM_LM_DIAG_SIZE 63
+
+#define BCM_AUTOSUSPEND_DELAY	5000 /* default autosleep delay */
+
 struct bcm_device {
 struct bcm_device {
 	struct list_head	list;
 	struct list_head	list;
 
 
@@ -55,7 +62,7 @@ struct bcm_device {
 	int			irq;
 	int			irq;
 	u8			irq_polarity;
 	u8			irq_polarity;
 
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
 	struct hci_uart		*hu;
 	struct hci_uart		*hu;
 	bool			is_suspended; /* suspend/resume flag */
 	bool			is_suspended; /* suspend/resume flag */
 #endif
 #endif
@@ -152,13 +159,17 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
 	return 0;
 	return 0;
 }
 }
 
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
 static irqreturn_t bcm_host_wake(int irq, void *data)
 static irqreturn_t bcm_host_wake(int irq, void *data)
 {
 {
 	struct bcm_device *bdev = data;
 	struct bcm_device *bdev = data;
 
 
 	bt_dev_dbg(bdev, "Host wake IRQ");
 	bt_dev_dbg(bdev, "Host wake IRQ");
 
 
+	pm_runtime_get(&bdev->pdev->dev);
+	pm_runtime_mark_last_busy(&bdev->pdev->dev);
+	pm_runtime_put_autosuspend(&bdev->pdev->dev);
+
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -182,6 +193,12 @@ static int bcm_request_irq(struct bcm_data *bcm)
 			goto unlock;
 			goto unlock;
 
 
 		device_init_wakeup(&bdev->pdev->dev, true);
 		device_init_wakeup(&bdev->pdev->dev, true);
+
+		pm_runtime_set_autosuspend_delay(&bdev->pdev->dev,
+						 BCM_AUTOSUSPEND_DELAY);
+		pm_runtime_use_autosuspend(&bdev->pdev->dev);
+		pm_runtime_set_active(&bdev->pdev->dev);
+		pm_runtime_enable(&bdev->pdev->dev);
 	}
 	}
 
 
 unlock:
 unlock:
@@ -197,7 +214,7 @@ static const struct bcm_set_sleep_mode default_sleep_params = {
 	.bt_wake_active = 1,	/* BT_WAKE active mode: 1 = high, 0 = low */
 	.bt_wake_active = 1,	/* BT_WAKE active mode: 1 = high, 0 = low */
 	.host_wake_active = 0,	/* HOST_WAKE active mode: 1 = high, 0 = low */
 	.host_wake_active = 0,	/* HOST_WAKE active mode: 1 = high, 0 = low */
 	.allow_host_sleep = 1,	/* Allow host sleep in SCO flag */
 	.allow_host_sleep = 1,	/* Allow host sleep in SCO flag */
-	.combine_modes = 0,	/* Combine sleep and LPM flag */
+	.combine_modes = 1,	/* Combine sleep and LPM flag */
 	.tristate_control = 0,	/* Allow tri-state control of UART tx flag */
 	.tristate_control = 0,	/* Allow tri-state control of UART tx flag */
 	/* Irrelevant USB flags */
 	/* Irrelevant USB flags */
 	.usb_auto_sleep = 0,
 	.usb_auto_sleep = 0,
@@ -232,6 +249,29 @@ static inline int bcm_request_irq(struct bcm_data *bcm) { return 0; }
 static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; }
 static inline int bcm_setup_sleep(struct hci_uart *hu) { return 0; }
 #endif
 #endif
 
 
+static int bcm_set_diag(struct hci_dev *hdev, bool enable)
+{
+	struct hci_uart *hu = hci_get_drvdata(hdev);
+	struct bcm_data *bcm = hu->priv;
+	struct sk_buff *skb;
+
+	if (!test_bit(HCI_RUNNING, &hdev->flags))
+		return -ENETDOWN;
+
+	skb = bt_skb_alloc(3, GFP_KERNEL);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	*skb_put(skb, 1) = BCM_LM_DIAG_PKT;
+	*skb_put(skb, 1) = 0xf0;
+	*skb_put(skb, 1) = enable;
+
+	skb_queue_tail(&bcm->txq, skb);
+	hci_uart_tx_wakeup(hu);
+
+	return 0;
+}
+
 static int bcm_open(struct hci_uart *hu)
 static int bcm_open(struct hci_uart *hu)
 {
 {
 	struct bcm_data *bcm;
 	struct bcm_data *bcm;
@@ -258,7 +298,7 @@ static int bcm_open(struct hci_uart *hu)
 		if (hu->tty->dev->parent == dev->pdev->dev.parent) {
 		if (hu->tty->dev->parent == dev->pdev->dev.parent) {
 			bcm->dev = dev;
 			bcm->dev = dev;
 			hu->init_speed = dev->init_speed;
 			hu->init_speed = dev->init_speed;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
 			dev->hu = hu;
 			dev->hu = hu;
 #endif
 #endif
 			bcm_gpio_set_power(bcm->dev, true);
 			bcm_gpio_set_power(bcm->dev, true);
@@ -282,7 +322,10 @@ static int bcm_close(struct hci_uart *hu)
 	mutex_lock(&bcm_device_lock);
 	mutex_lock(&bcm_device_lock);
 	if (bcm_device_exists(bdev)) {
 	if (bcm_device_exists(bdev)) {
 		bcm_gpio_set_power(bdev, false);
 		bcm_gpio_set_power(bdev, false);
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
+		pm_runtime_disable(&bdev->pdev->dev);
+		pm_runtime_set_suspended(&bdev->pdev->dev);
+
 		if (device_can_wakeup(&bdev->pdev->dev)) {
 		if (device_can_wakeup(&bdev->pdev->dev)) {
 			devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
 			devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
 			device_init_wakeup(&bdev->pdev->dev, false);
 			device_init_wakeup(&bdev->pdev->dev, false);
@@ -322,6 +365,7 @@ static int bcm_setup(struct hci_uart *hu)
 
 
 	bt_dev_dbg(hu->hdev, "hu %p", hu);
 	bt_dev_dbg(hu->hdev, "hu %p", hu);
 
 
+	hu->hdev->set_diag = bcm_set_diag;
 	hu->hdev->set_bdaddr = btbcm_set_bdaddr;
 	hu->hdev->set_bdaddr = btbcm_set_bdaddr;
 
 
 	err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
 	err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
@@ -379,10 +423,18 @@ finalize:
 	return err;
 	return err;
 }
 }
 
 
+#define BCM_RECV_LM_DIAG \
+	.type = BCM_LM_DIAG_PKT, \
+	.hlen = BCM_LM_DIAG_SIZE, \
+	.loff = 0, \
+	.lsize = 0, \
+	.maxlen = BCM_LM_DIAG_SIZE
+
 static const struct h4_recv_pkt bcm_recv_pkts[] = {
 static const struct h4_recv_pkt bcm_recv_pkts[] = {
-	{ H4_RECV_ACL,   .recv = hci_recv_frame },
-	{ H4_RECV_SCO,   .recv = hci_recv_frame },
-	{ H4_RECV_EVENT, .recv = hci_recv_frame },
+	{ H4_RECV_ACL,      .recv = hci_recv_frame },
+	{ H4_RECV_SCO,      .recv = hci_recv_frame },
+	{ H4_RECV_EVENT,    .recv = hci_recv_frame },
+	{ BCM_RECV_LM_DIAG, .recv = hci_recv_diag  },
 };
 };
 
 
 static int bcm_recv(struct hci_uart *hu, const void *data, int count)
 static int bcm_recv(struct hci_uart *hu, const void *data, int count)
@@ -399,6 +451,15 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
 		bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
 		bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
 		bcm->rx_skb = NULL;
 		bcm->rx_skb = NULL;
 		return err;
 		return err;
+	} else if (!bcm->rx_skb) {
+		/* Delay auto-suspend when receiving completed packet */
+		mutex_lock(&bcm_device_lock);
+		if (bcm->dev && bcm_device_exists(bcm->dev)) {
+			pm_runtime_get(&bcm->dev->pdev->dev);
+			pm_runtime_mark_last_busy(&bcm->dev->pdev->dev);
+			pm_runtime_put_autosuspend(&bcm->dev->pdev->dev);
+		}
+		mutex_unlock(&bcm_device_lock);
 	}
 	}
 
 
 	return count;
 	return count;
@@ -420,28 +481,40 @@ static int bcm_enqueue(struct hci_uart *hu, struct sk_buff *skb)
 static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
 static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
 {
 {
 	struct bcm_data *bcm = hu->priv;
 	struct bcm_data *bcm = hu->priv;
+	struct sk_buff *skb = NULL;
+	struct bcm_device *bdev = NULL;
+
+	mutex_lock(&bcm_device_lock);
+
+	if (bcm_device_exists(bcm->dev)) {
+		bdev = bcm->dev;
+		pm_runtime_get_sync(&bdev->pdev->dev);
+		/* Shall be resumed here */
+	}
+
+	skb = skb_dequeue(&bcm->txq);
+
+	if (bdev) {
+		pm_runtime_mark_last_busy(&bdev->pdev->dev);
+		pm_runtime_put_autosuspend(&bdev->pdev->dev);
+	}
 
 
-	return skb_dequeue(&bcm->txq);
+	mutex_unlock(&bcm_device_lock);
+
+	return skb;
 }
 }
 
 
-#ifdef CONFIG_PM_SLEEP
-/* Platform suspend callback */
-static int bcm_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int bcm_suspend_device(struct device *dev)
 {
 {
 	struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
 	struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
-	int error;
-
-	bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended);
-
-	mutex_lock(&bcm_device_lock);
 
 
-	if (!bdev->hu)
-		goto unlock;
+	bt_dev_dbg(bdev, "");
 
 
-	if (!bdev->is_suspended) {
+	if (!bdev->is_suspended && bdev->hu) {
 		hci_uart_set_flow_control(bdev->hu, true);
 		hci_uart_set_flow_control(bdev->hu, true);
 
 
-		/* Once this callback returns, driver suspends BT via GPIO */
+		/* Once this returns, driver suspends BT via GPIO */
 		bdev->is_suspended = true;
 		bdev->is_suspended = true;
 	}
 	}
 
 
@@ -452,6 +525,53 @@ static int bcm_suspend(struct device *dev)
 		mdelay(15);
 		mdelay(15);
 	}
 	}
 
 
+	return 0;
+}
+
+static int bcm_resume_device(struct device *dev)
+{
+	struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
+
+	bt_dev_dbg(bdev, "");
+
+	if (bdev->device_wakeup) {
+		gpiod_set_value(bdev->device_wakeup, true);
+		bt_dev_dbg(bdev, "resume, delaying 15 ms");
+		mdelay(15);
+	}
+
+	/* When this executes, the device has woken up already */
+	if (bdev->is_suspended && bdev->hu) {
+		bdev->is_suspended = false;
+
+		hci_uart_set_flow_control(bdev->hu, false);
+	}
+
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+/* Platform suspend callback */
+static int bcm_suspend(struct device *dev)
+{
+	struct bcm_device *bdev = platform_get_drvdata(to_platform_device(dev));
+	int error;
+
+	bt_dev_dbg(bdev, "suspend: is_suspended %d", bdev->is_suspended);
+
+	/* bcm_suspend can be called at any time as long as platform device is
+	 * bound, so it should use bcm_device_lock to protect access to hci_uart
+	 * and device_wake-up GPIO.
+	 */
+	mutex_lock(&bcm_device_lock);
+
+	if (!bdev->hu)
+		goto unlock;
+
+	if (pm_runtime_active(dev))
+		bcm_suspend_device(dev);
+
 	if (device_may_wakeup(&bdev->pdev->dev)) {
 	if (device_may_wakeup(&bdev->pdev->dev)) {
 		error = enable_irq_wake(bdev->irq);
 		error = enable_irq_wake(bdev->irq);
 		if (!error)
 		if (!error)
@@ -471,6 +591,10 @@ static int bcm_resume(struct device *dev)
 
 
 	bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);
 	bt_dev_dbg(bdev, "resume: is_suspended %d", bdev->is_suspended);
 
 
+	/* bcm_resume can be called at any time as long as platform device is
+	 * bound, so it should use bcm_device_lock to protect access to hci_uart
+	 * and device_wake-up GPIO.
+	 */
 	mutex_lock(&bcm_device_lock);
 	mutex_lock(&bcm_device_lock);
 
 
 	if (!bdev->hu)
 	if (!bdev->hu)
@@ -481,22 +605,15 @@ static int bcm_resume(struct device *dev)
 		bt_dev_dbg(bdev, "BCM irq: disabled");
 		bt_dev_dbg(bdev, "BCM irq: disabled");
 	}
 	}
 
 
-	if (bdev->device_wakeup) {
-		gpiod_set_value(bdev->device_wakeup, true);
-		bt_dev_dbg(bdev, "resume, delaying 15 ms");
-		mdelay(15);
-	}
-
-	/* When this callback executes, the device has woken up already */
-	if (bdev->is_suspended) {
-		bdev->is_suspended = false;
-
-		hci_uart_set_flow_control(bdev->hu, false);
-	}
+	bcm_resume_device(dev);
 
 
 unlock:
 unlock:
 	mutex_unlock(&bcm_device_lock);
 	mutex_unlock(&bcm_device_lock);
 
 
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
 	return 0;
 	return 0;
 }
 }
 #endif
 #endif
@@ -513,6 +630,22 @@ static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
 };
 };
 
 
 #ifdef CONFIG_ACPI
 #ifdef CONFIG_ACPI
+static u8 acpi_active_low = ACPI_ACTIVE_LOW;
+
+/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
+static const struct dmi_system_id bcm_wrong_irq_dmi_table[] = {
+	{
+		.ident = "Asus T100TA",
+		.matches = {
+			DMI_EXACT_MATCH(DMI_SYS_VENDOR,
+					"ASUSTeK COMPUTER INC."),
+			DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+		},
+		.driver_data = &acpi_active_low,
+	},
+	{ }
+};
+
 static int bcm_resource(struct acpi_resource *ares, void *data)
 static int bcm_resource(struct acpi_resource *ares, void *data)
 {
 {
 	struct bcm_device *dev = data;
 	struct bcm_device *dev = data;
@@ -549,15 +682,10 @@ static int bcm_resource(struct acpi_resource *ares, void *data)
 static int bcm_acpi_probe(struct bcm_device *dev)
 static int bcm_acpi_probe(struct bcm_device *dev)
 {
 {
 	struct platform_device *pdev = dev->pdev;
 	struct platform_device *pdev = dev->pdev;
-	const struct acpi_device_id *id;
-	struct acpi_device *adev;
 	LIST_HEAD(resources);
 	LIST_HEAD(resources);
+	const struct dmi_system_id *dmi_id;
 	int ret;
 	int ret;
 
 
-	id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
-	if (!id)
-		return -ENODEV;
-
 	/* Retrieve GPIO data */
 	/* Retrieve GPIO data */
 	dev->name = dev_name(&pdev->dev);
 	dev->name = dev_name(&pdev->dev);
 	ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
 	ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev),
@@ -602,11 +730,18 @@ static int bcm_acpi_probe(struct bcm_device *dev)
 	}
 	}
 
 
 	/* Retrieve UART ACPI info */
 	/* Retrieve UART ACPI info */
-	adev = ACPI_COMPANION(&dev->pdev->dev);
-	if (!adev)
-		return 0;
+	ret = acpi_dev_get_resources(ACPI_COMPANION(&dev->pdev->dev),
+				     &resources, bcm_resource, dev);
+	if (ret < 0)
+		return ret;
+	acpi_dev_free_resource_list(&resources);
 
 
-	acpi_dev_get_resources(adev, &resources, bcm_resource, dev);
+	dmi_id = dmi_first_match(bcm_wrong_irq_dmi_table);
+	if (dmi_id) {
+		bt_dev_warn(dev, "%s: Overwriting IRQ polarity to active low",
+			    dmi_id->ident);
+		dev->irq_polarity = *(u8 *)dmi_id->driver_data;
+	}
 
 
 	return 0;
 	return 0;
 }
 }
@@ -620,7 +755,6 @@ static int bcm_acpi_probe(struct bcm_device *dev)
 static int bcm_probe(struct platform_device *pdev)
 static int bcm_probe(struct platform_device *pdev)
 {
 {
 	struct bcm_device *dev;
 	struct bcm_device *dev;
-	struct acpi_device_id *pdata = pdev->dev.platform_data;
 	int ret;
 	int ret;
 
 
 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
 	dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -629,15 +763,9 @@ static int bcm_probe(struct platform_device *pdev)
 
 
 	dev->pdev = pdev;
 	dev->pdev = pdev;
 
 
-	if (ACPI_HANDLE(&pdev->dev)) {
-		ret = bcm_acpi_probe(dev);
-		if (ret)
-			return ret;
-	} else if (pdata) {
-		dev->name = pdata->id;
-	} else {
-		return -ENODEV;
-	}
+	ret = bcm_acpi_probe(dev);
+	if (ret)
+		return ret;
 
 
 	platform_set_drvdata(pdev, dev);
 	platform_set_drvdata(pdev, dev);
 
 
@@ -693,7 +821,10 @@ MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
 #endif
 #endif
 
 
 /* Platform suspend and resume callbacks */
 /* Platform suspend and resume callbacks */
-static SIMPLE_DEV_PM_OPS(bcm_pm_ops, bcm_suspend, bcm_resume);
+static const struct dev_pm_ops bcm_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(bcm_suspend, bcm_resume)
+	SET_RUNTIME_PM_OPS(bcm_suspend_device, bcm_resume_device, NULL)
+};
 
 
 static struct platform_driver bcm_driver = {
 static struct platform_driver bcm_driver = {
 	.probe = bcm_probe,
 	.probe = bcm_probe,

+ 1 - 0
drivers/bluetooth/hci_h4.c

@@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
 
 
 	return skb;
 	return skb;
 }
 }
+EXPORT_SYMBOL_GPL(h4_recv_buf);

+ 5 - 5
drivers/bluetooth/hci_h5.c

@@ -128,7 +128,7 @@ static void h5_timed_event(unsigned long arg)
 {
 {
 	const unsigned char sync_req[] = { 0x01, 0x7e };
 	const unsigned char sync_req[] = { 0x01, 0x7e };
 	unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
 	unsigned char conf_req[] = { 0x03, 0xfc, 0x01 };
-	struct hci_uart *hu = (struct hci_uart *) arg;
+	struct hci_uart *hu = (struct hci_uart *)arg;
 	struct h5 *h5 = hu->priv;
 	struct h5 *h5 = hu->priv;
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	unsigned long flags;
 	unsigned long flags;
@@ -210,7 +210,7 @@ static int h5_open(struct hci_uart *hu)
 
 
 	init_timer(&h5->timer);
 	init_timer(&h5->timer);
 	h5->timer.function = h5_timed_event;
 	h5->timer.function = h5_timed_event;
-	h5->timer.data = (unsigned long) hu;
+	h5->timer.data = (unsigned long)hu;
 
 
 	h5->tx_win = H5_TX_WIN_MAX;
 	h5->tx_win = H5_TX_WIN_MAX;
 
 
@@ -453,7 +453,7 @@ static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c)
 		return -ENOMEM;
 		return -ENOMEM;
 	}
 	}
 
 
-	h5->rx_skb->dev = (void *) hu->hdev;
+	h5->rx_skb->dev = (void *)hu->hdev;
 
 
 	return 0;
 	return 0;
 }
 }
@@ -696,7 +696,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
 	}
 	}
 
 
 	skb = skb_dequeue(&h5->unrel);
 	skb = skb_dequeue(&h5->unrel);
-	if (skb != NULL) {
+	if (skb) {
 		nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
 		nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
 				      skb->data, skb->len);
 				      skb->data, skb->len);
 		if (nskb) {
 		if (nskb) {
@@ -714,7 +714,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
 		goto unlock;
 		goto unlock;
 
 
 	skb = skb_dequeue(&h5->rel);
 	skb = skb_dequeue(&h5->rel);
-	if (skb != NULL) {
+	if (skb) {
 		nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
 		nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
 				      skb->data, skb->len);
 				      skb->data, skb->len);
 		if (nskb) {
 		if (nskb) {

+ 0 - 24
drivers/bluetooth/hci_intel.c

@@ -1165,22 +1165,6 @@ static const struct acpi_device_id intel_acpi_match[] = {
 	{ },
 	{ },
 };
 };
 MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
 MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
-
-static int intel_acpi_probe(struct intel_device *idev)
-{
-	const struct acpi_device_id *id;
-
-	id = acpi_match_device(intel_acpi_match, &idev->pdev->dev);
-	if (!id)
-		return -ENODEV;
-
-	return 0;
-}
-#else
-static int intel_acpi_probe(struct intel_device *idev)
-{
-	return -ENODEV;
-}
 #endif
 #endif
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
@@ -1248,14 +1232,6 @@ static int intel_probe(struct platform_device *pdev)
 
 
 	idev->pdev = pdev;
 	idev->pdev = pdev;
 
 
-	if (ACPI_HANDLE(&pdev->dev)) {
-		int err = intel_acpi_probe(idev);
-		if (err)
-			return err;
-	} else {
-		return -ENODEV;
-	}
-
 	idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset",
 	idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset",
 					      GPIOD_OUT_LOW);
 					      GPIOD_OUT_LOW);
 	if (IS_ERR(idev->reset)) {
 	if (IS_ERR(idev->reset)) {

+ 3 - 14
drivers/bluetooth/hci_ldisc.c

@@ -208,9 +208,6 @@ static int hci_uart_open(struct hci_dev *hdev)
 	BT_DBG("%s %p", hdev->name, hdev);
 	BT_DBG("%s %p", hdev->name, hdev);
 
 
 	/* Nothing to do for UART driver */
 	/* Nothing to do for UART driver */
-
-	set_bit(HCI_RUNNING, &hdev->flags);
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -241,9 +238,6 @@ static int hci_uart_close(struct hci_dev *hdev)
 {
 {
 	BT_DBG("hdev %p", hdev);
 	BT_DBG("hdev %p", hdev);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	hci_uart_flush(hdev);
 	hci_uart_flush(hdev);
 	hdev->flush = NULL;
 	hdev->flush = NULL;
 	return 0;
 	return 0;
@@ -254,9 +248,6 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
 {
 	struct hci_uart *hu = hci_get_drvdata(hdev);
 	struct hci_uart *hu = hci_get_drvdata(hdev);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 	BT_DBG("%s: type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len);
 
 
 	hu->proto->enqueue(hu, skb);
 	hu->proto->enqueue(hu, skb);
@@ -470,8 +461,6 @@ static int hci_uart_tty_open(struct tty_struct *tty)
 	INIT_WORK(&hu->init_ready, hci_uart_init_work);
 	INIT_WORK(&hu->init_ready, hci_uart_init_work);
 	INIT_WORK(&hu->write_work, hci_uart_write_work);
 	INIT_WORK(&hu->write_work, hci_uart_write_work);
 
 
-	spin_lock_init(&hu->rx_lock);
-
 	/* Flush any pending characters in the driver and line discipline. */
 	/* Flush any pending characters in the driver and line discipline. */
 
 
 	/* FIXME: why is this needed. Note don't use ldisc_ref here as the
 	/* FIXME: why is this needed. Note don't use ldisc_ref here as the
@@ -569,14 +558,14 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data,
 	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
 	if (!test_bit(HCI_UART_PROTO_SET, &hu->flags))
 		return;
 		return;
 
 
-	spin_lock(&hu->rx_lock);
+	/* It does not need a lock here as it is already protected by a mutex in
+	 * tty caller
+	 */
 	hu->proto->recv(hu, data, count);
 	hu->proto->recv(hu, data, count);
 
 
 	if (hu->hdev)
 	if (hu->hdev)
 		hu->hdev->stat.byte_rx += count;
 		hu->hdev->stat.byte_rx += count;
 
 
-	spin_unlock(&hu->rx_lock);
-
 	tty_unthrottle(tty);
 	tty_unthrottle(tty);
 }
 }
 
 

+ 2 - 2
drivers/bluetooth/hci_qca.c

@@ -347,7 +347,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
 	struct hci_uart *hu = (struct hci_uart *)arg;
 	struct hci_uart *hu = (struct hci_uart *)arg;
 	struct qca_data *qca = hu->priv;
 	struct qca_data *qca = hu->priv;
 	unsigned long flags, retrans_delay;
 	unsigned long flags, retrans_delay;
-	unsigned long retransmit = 0;
+	bool retransmit = false;
 
 
 	BT_DBG("hu %p wake retransmit timeout in %d state",
 	BT_DBG("hu %p wake retransmit timeout in %d state",
 		hu, qca->tx_ibs_state);
 		hu, qca->tx_ibs_state);
@@ -358,7 +358,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
 	switch (qca->tx_ibs_state) {
 	switch (qca->tx_ibs_state) {
 	case HCI_IBS_TX_WAKING:
 	case HCI_IBS_TX_WAKING:
 		/* No WAKE_ACK, retransmit WAKE */
 		/* No WAKE_ACK, retransmit WAKE */
-		retransmit = 1;
+		retransmit = true;
 		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
 		if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
 			BT_ERR("Failed to acknowledge device wake up");
 			BT_ERR("Failed to acknowledge device wake up");
 			break;
 			break;

+ 0 - 1
drivers/bluetooth/hci_uart.h

@@ -85,7 +85,6 @@ struct hci_uart {
 
 
 	struct sk_buff		*tx_skb;
 	struct sk_buff		*tx_skb;
 	unsigned long		tx_state;
 	unsigned long		tx_state;
-	spinlock_t		rx_lock;
 
 
 	unsigned int init_speed;
 	unsigned int init_speed;
 	unsigned int oper_speed;
 	unsigned int oper_speed;

+ 0 - 8
drivers/bluetooth/hci_vhci.c

@@ -55,8 +55,6 @@ struct vhci_data {
 
 
 static int vhci_open_dev(struct hci_dev *hdev)
 static int vhci_open_dev(struct hci_dev *hdev)
 {
 {
-	set_bit(HCI_RUNNING, &hdev->flags);
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -64,9 +62,6 @@ static int vhci_close_dev(struct hci_dev *hdev)
 {
 {
 	struct vhci_data *data = hci_get_drvdata(hdev);
 	struct vhci_data *data = hci_get_drvdata(hdev);
 
 
-	if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-		return 0;
-
 	skb_queue_purge(&data->readq);
 	skb_queue_purge(&data->readq);
 
 
 	return 0;
 	return 0;
@@ -85,9 +80,6 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 {
 {
 	struct vhci_data *data = hci_get_drvdata(hdev);
 	struct vhci_data *data = hci_get_drvdata(hdev);
 
 
-	if (!test_bit(HCI_RUNNING, &hdev->flags))
-		return -EBUSY;
-
 	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
 	memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
 	skb_queue_tail(&data->readq, skb);
 	skb_queue_tail(&data->readq, skb);
 
 

+ 1 - 0
drivers/net/ieee802154/Kconfig

@@ -43,6 +43,7 @@ config IEEE802154_MRF24J40
 	tristate "Microchip MRF24J40 transceiver driver"
 	tristate "Microchip MRF24J40 transceiver driver"
 	depends on IEEE802154_DRIVERS && MAC802154
 	depends on IEEE802154_DRIVERS && MAC802154
 	depends on SPI
 	depends on SPI
+	select REGMAP_SPI
 	---help---
 	---help---
 	  Say Y here to enable the MRF24J20 SPI 802.15.4 wireless
 	  Say Y here to enable the MRF24J20 SPI 802.15.4 wireless
 	  controller.
 	  controller.

+ 89 - 122
drivers/net/ieee802154/at86rf230.c

@@ -81,7 +81,7 @@ struct at86rf230_state_change {
 	u8 from_state;
 	u8 from_state;
 	u8 to_state;
 	u8 to_state;
 
 
-	bool irq_enable;
+	bool free;
 };
 };
 
 
 struct at86rf230_trac {
 struct at86rf230_trac {
@@ -105,8 +105,6 @@ struct at86rf230_local {
 	struct completion state_complete;
 	struct completion state_complete;
 	struct at86rf230_state_change state;
 	struct at86rf230_state_change state;
 
 
-	struct at86rf230_state_change irq;
-
 	unsigned long cal_timeout;
 	unsigned long cal_timeout;
 	bool is_tx;
 	bool is_tx;
 	bool is_tx_from_off;
 	bool is_tx_from_off;
@@ -122,8 +120,7 @@ struct at86rf230_local {
 static void
 static void
 at86rf230_async_state_change(struct at86rf230_local *lp,
 at86rf230_async_state_change(struct at86rf230_local *lp,
 			     struct at86rf230_state_change *ctx,
 			     struct at86rf230_state_change *ctx,
-			     const u8 state, void (*complete)(void *context),
-			     const bool irq_enable);
+			     const u8 state, void (*complete)(void *context));
 
 
 static inline void
 static inline void
 at86rf230_sleep(struct at86rf230_local *lp)
 at86rf230_sleep(struct at86rf230_local *lp)
@@ -352,8 +349,10 @@ at86rf230_async_error_recover(void *context)
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 
 
 	lp->is_tx = 0;
 	lp->is_tx = 0;
-	at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL, false);
+	at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
 	ieee802154_wake_queue(lp->hw);
 	ieee802154_wake_queue(lp->hw);
+	if (ctx->free)
+		kfree(ctx);
 }
 }
 
 
 static inline void
 static inline void
@@ -363,15 +362,14 @@ at86rf230_async_error(struct at86rf230_local *lp,
 	dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
 	dev_err(&lp->spi->dev, "spi_async error %d\n", rc);
 
 
 	at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
 	at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
-				     at86rf230_async_error_recover, false);
+				     at86rf230_async_error_recover);
 }
 }
 
 
 /* Generic function to get some register value in async mode */
 /* Generic function to get some register value in async mode */
 static void
 static void
-at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
+at86rf230_async_read_reg(struct at86rf230_local *lp, u8 reg,
 			 struct at86rf230_state_change *ctx,
 			 struct at86rf230_state_change *ctx,
-			 void (*complete)(void *context),
-			 const bool irq_enable)
+			 void (*complete)(void *context))
 {
 {
 	int rc;
 	int rc;
 
 
@@ -379,14 +377,24 @@ at86rf230_async_read_reg(struct at86rf230_local *lp, const u8 reg,
 
 
 	tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
 	tx_buf[0] = (reg & CMD_REG_MASK) | CMD_REG;
 	ctx->msg.complete = complete;
 	ctx->msg.complete = complete;
-	ctx->irq_enable = irq_enable;
 	rc = spi_async(lp->spi, &ctx->msg);
 	rc = spi_async(lp->spi, &ctx->msg);
-	if (rc) {
-		if (irq_enable)
-			enable_irq(ctx->irq);
+	if (rc)
+		at86rf230_async_error(lp, ctx, rc);
+}
 
 
+static void
+at86rf230_async_write_reg(struct at86rf230_local *lp, u8 reg, u8 val,
+			  struct at86rf230_state_change *ctx,
+			  void (*complete)(void *context))
+{
+	int rc;
+
+	ctx->buf[0] = (reg & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
+	ctx->buf[1] = val;
+	ctx->msg.complete = complete;
+	rc = spi_async(lp->spi, &ctx->msg);
+	if (rc)
 		at86rf230_async_error(lp, ctx, rc);
 		at86rf230_async_error(lp, ctx, rc);
-	}
 }
 }
 
 
 static void
 static void
@@ -434,8 +442,7 @@ at86rf230_async_state_assert(void *context)
 				lp->tx_retry++;
 				lp->tx_retry++;
 
 
 				at86rf230_async_state_change(lp, ctx, state,
 				at86rf230_async_state_change(lp, ctx, state,
-							     ctx->complete,
-							     ctx->irq_enable);
+							     ctx->complete);
 				return;
 				return;
 			}
 			}
 		}
 		}
@@ -456,8 +463,7 @@ static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer)
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 
 
 	at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
 	at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-				 at86rf230_async_state_assert,
-				 ctx->irq_enable);
+				 at86rf230_async_state_assert);
 
 
 	return HRTIMER_NORESTART;
 	return HRTIMER_NORESTART;
 }
 }
@@ -562,14 +568,12 @@ at86rf230_async_state_change_start(void *context)
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 	u8 *buf = ctx->buf;
 	u8 *buf = ctx->buf;
 	const u8 trx_state = buf[1] & TRX_STATE_MASK;
 	const u8 trx_state = buf[1] & TRX_STATE_MASK;
-	int rc;
 
 
 	/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
 	/* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
 	if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
 	if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
 		udelay(1);
 		udelay(1);
 		at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
 		at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-					 at86rf230_async_state_change_start,
-					 ctx->irq_enable);
+					 at86rf230_async_state_change_start);
 		return;
 		return;
 	}
 	}
 
 
@@ -586,31 +590,20 @@ at86rf230_async_state_change_start(void *context)
 	/* Going into the next step for a state change which do a timing
 	/* Going into the next step for a state change which do a timing
 	 * relevant delay.
 	 * relevant delay.
 	 */
 	 */
-	buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
-	buf[1] = ctx->to_state;
-	ctx->msg.complete = at86rf230_async_state_delay;
-	rc = spi_async(lp->spi, &ctx->msg);
-	if (rc) {
-		if (ctx->irq_enable)
-			enable_irq(ctx->irq);
-
-		at86rf230_async_error(lp, ctx, rc);
-	}
+	at86rf230_async_write_reg(lp, RG_TRX_STATE, ctx->to_state, ctx,
+				  at86rf230_async_state_delay);
 }
 }
 
 
 static void
 static void
 at86rf230_async_state_change(struct at86rf230_local *lp,
 at86rf230_async_state_change(struct at86rf230_local *lp,
 			     struct at86rf230_state_change *ctx,
 			     struct at86rf230_state_change *ctx,
-			     const u8 state, void (*complete)(void *context),
-			     const bool irq_enable)
+			     const u8 state, void (*complete)(void *context))
 {
 {
 	/* Initialization for the state change context */
 	/* Initialization for the state change context */
 	ctx->to_state = state;
 	ctx->to_state = state;
 	ctx->complete = complete;
 	ctx->complete = complete;
-	ctx->irq_enable = irq_enable;
 	at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
 	at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-				 at86rf230_async_state_change_start,
-				 irq_enable);
+				 at86rf230_async_state_change_start);
 }
 }
 
 
 static void
 static void
@@ -632,8 +625,7 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
 	unsigned long rc;
 	unsigned long rc;
 
 
 	at86rf230_async_state_change(lp, &lp->state, state,
 	at86rf230_async_state_change(lp, &lp->state, state,
-				     at86rf230_sync_state_change_complete,
-				     false);
+				     at86rf230_sync_state_change_complete);
 
 
 	rc = wait_for_completion_timeout(&lp->state_complete,
 	rc = wait_for_completion_timeout(&lp->state_complete,
 					 msecs_to_jiffies(100));
 					 msecs_to_jiffies(100));
@@ -651,9 +643,8 @@ at86rf230_tx_complete(void *context)
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 
 
-	enable_irq(ctx->irq);
-
 	ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
 	ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
+	kfree(ctx);
 }
 }
 
 
 static void
 static void
@@ -663,7 +654,7 @@ at86rf230_tx_on(void *context)
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 
 
 	at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
 	at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
-				     at86rf230_tx_complete, true);
+				     at86rf230_tx_complete);
 }
 }
 
 
 static void
 static void
@@ -697,8 +688,7 @@ at86rf230_tx_trac_check(void *context)
 		}
 		}
 	}
 	}
 
 
-	at86rf230_async_state_change(lp, &lp->irq, STATE_TX_ON,
-				     at86rf230_tx_on, true);
+	at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_tx_on);
 }
 }
 
 
 static void
 static void
@@ -706,7 +696,6 @@ at86rf230_rx_read_frame_complete(void *context)
 {
 {
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
-	u8 rx_local_buf[AT86RF2XX_MAX_BUF];
 	const u8 *buf = ctx->buf;
 	const u8 *buf = ctx->buf;
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	u8 len, lqi;
 	u8 len, lqi;
@@ -718,18 +707,16 @@ at86rf230_rx_read_frame_complete(void *context)
 	}
 	}
 	lqi = buf[2 + len];
 	lqi = buf[2 + len];
 
 
-	memcpy(rx_local_buf, buf + 2, len);
-	ctx->trx.len = 2;
-	enable_irq(ctx->irq);
-
 	skb = dev_alloc_skb(IEEE802154_MTU);
 	skb = dev_alloc_skb(IEEE802154_MTU);
 	if (!skb) {
 	if (!skb) {
 		dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
 		dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
+		kfree(ctx);
 		return;
 		return;
 	}
 	}
 
 
-	memcpy(skb_put(skb, len), rx_local_buf, len);
+	memcpy(skb_put(skb, len), buf + 2, len);
 	ieee802154_rx_irqsafe(lp->hw, skb, lqi);
 	ieee802154_rx_irqsafe(lp->hw, skb, lqi);
+	kfree(ctx);
 }
 }
 
 
 static void
 static void
@@ -765,21 +752,23 @@ at86rf230_rx_trac_check(void *context)
 	rc = spi_async(lp->spi, &ctx->msg);
 	rc = spi_async(lp->spi, &ctx->msg);
 	if (rc) {
 	if (rc) {
 		ctx->trx.len = 2;
 		ctx->trx.len = 2;
-		enable_irq(ctx->irq);
 		at86rf230_async_error(lp, ctx, rc);
 		at86rf230_async_error(lp, ctx, rc);
 	}
 	}
 }
 }
 
 
 static void
 static void
-at86rf230_irq_trx_end(struct at86rf230_local *lp)
+at86rf230_irq_trx_end(void *context)
 {
 {
+	struct at86rf230_state_change *ctx = context;
+	struct at86rf230_local *lp = ctx->lp;
+
 	if (lp->is_tx) {
 	if (lp->is_tx) {
 		lp->is_tx = 0;
 		lp->is_tx = 0;
-		at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
-					 at86rf230_tx_trac_check, true);
+		at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+					 at86rf230_tx_trac_check);
 	} else {
 	} else {
-		at86rf230_async_read_reg(lp, RG_TRX_STATE, &lp->irq,
-					 at86rf230_rx_trac_check, true);
+		at86rf230_async_read_reg(lp, RG_TRX_STATE, ctx,
+					 at86rf230_rx_trac_check);
 	}
 	}
 }
 }
 
 
@@ -789,32 +778,59 @@ at86rf230_irq_status(void *context)
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 	const u8 *buf = ctx->buf;
 	const u8 *buf = ctx->buf;
-	const u8 irq = buf[1];
+	u8 irq = buf[1];
+
+	enable_irq(lp->spi->irq);
 
 
 	if (irq & IRQ_TRX_END) {
 	if (irq & IRQ_TRX_END) {
-		at86rf230_irq_trx_end(lp);
+		at86rf230_irq_trx_end(ctx);
 	} else {
 	} else {
-		enable_irq(ctx->irq);
 		dev_err(&lp->spi->dev, "not supported irq %02x received\n",
 		dev_err(&lp->spi->dev, "not supported irq %02x received\n",
 			irq);
 			irq);
+		kfree(ctx);
 	}
 	}
 }
 }
 
 
+static void
+at86rf230_setup_spi_messages(struct at86rf230_local *lp,
+			     struct at86rf230_state_change *state)
+{
+	state->lp = lp;
+	state->irq = lp->spi->irq;
+	spi_message_init(&state->msg);
+	state->msg.context = state;
+	state->trx.len = 2;
+	state->trx.tx_buf = state->buf;
+	state->trx.rx_buf = state->buf;
+	spi_message_add_tail(&state->trx, &state->msg);
+	hrtimer_init(&state->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+	state->timer.function = at86rf230_async_state_timer;
+}
+
 static irqreturn_t at86rf230_isr(int irq, void *data)
 static irqreturn_t at86rf230_isr(int irq, void *data)
 {
 {
 	struct at86rf230_local *lp = data;
 	struct at86rf230_local *lp = data;
-	struct at86rf230_state_change *ctx = &lp->irq;
-	u8 *buf = ctx->buf;
+	struct at86rf230_state_change *ctx;
 	int rc;
 	int rc;
 
 
 	disable_irq_nosync(irq);
 	disable_irq_nosync(irq);
 
 
-	buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
+	ctx = kzalloc(sizeof(*ctx), GFP_ATOMIC);
+	if (!ctx) {
+		enable_irq(irq);
+		return IRQ_NONE;
+	}
+
+	at86rf230_setup_spi_messages(lp, ctx);
+	/* tell on error handling to free ctx */
+	ctx->free = true;
+
+	ctx->buf[0] = (RG_IRQ_STATUS & CMD_REG_MASK) | CMD_REG;
 	ctx->msg.complete = at86rf230_irq_status;
 	ctx->msg.complete = at86rf230_irq_status;
 	rc = spi_async(lp->spi, &ctx->msg);
 	rc = spi_async(lp->spi, &ctx->msg);
 	if (rc) {
 	if (rc) {
-		enable_irq(irq);
 		at86rf230_async_error(lp, ctx, rc);
 		at86rf230_async_error(lp, ctx, rc);
+		enable_irq(irq);
 		return IRQ_NONE;
 		return IRQ_NONE;
 	}
 	}
 
 
@@ -826,21 +842,14 @@ at86rf230_write_frame_complete(void *context)
 {
 {
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_state_change *ctx = context;
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
-	u8 *buf = ctx->buf;
-	int rc;
 
 
 	ctx->trx.len = 2;
 	ctx->trx.len = 2;
 
 
-	if (gpio_is_valid(lp->slp_tr)) {
+	if (gpio_is_valid(lp->slp_tr))
 		at86rf230_slp_tr_rising_edge(lp);
 		at86rf230_slp_tr_rising_edge(lp);
-	} else {
-		buf[0] = (RG_TRX_STATE & CMD_REG_MASK) | CMD_REG | CMD_WRITE;
-		buf[1] = STATE_BUSY_TX;
-		ctx->msg.complete = NULL;
-		rc = spi_async(lp->spi, &ctx->msg);
-		if (rc)
-			at86rf230_async_error(lp, ctx, rc);
-	}
+	else
+		at86rf230_async_write_reg(lp, RG_TRX_STATE, STATE_BUSY_TX, ctx,
+					  NULL);
 }
 }
 
 
 static void
 static void
@@ -873,7 +882,7 @@ at86rf230_xmit_tx_on(void *context)
 	struct at86rf230_local *lp = ctx->lp;
 	struct at86rf230_local *lp = ctx->lp;
 
 
 	at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
 	at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
-				     at86rf230_write_frame, false);
+				     at86rf230_write_frame);
 }
 }
 
 
 static void
 static void
@@ -886,12 +895,10 @@ at86rf230_xmit_start(void *context)
 	if (lp->is_tx_from_off) {
 	if (lp->is_tx_from_off) {
 		lp->is_tx_from_off = false;
 		lp->is_tx_from_off = false;
 		at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
 		at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
-					     at86rf230_write_frame,
-					     false);
+					     at86rf230_write_frame);
 	} else {
 	} else {
 		at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
 		at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
-					     at86rf230_xmit_tx_on,
-					     false);
+					     at86rf230_xmit_tx_on);
 	}
 	}
 }
 }
 
 
@@ -914,7 +921,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
 	if (time_is_before_jiffies(lp->cal_timeout)) {
 	if (time_is_before_jiffies(lp->cal_timeout)) {
 		lp->is_tx_from_off = true;
 		lp->is_tx_from_off = true;
 		at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
 		at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
-					     at86rf230_xmit_start, false);
+					     at86rf230_xmit_start);
 	} else {
 	} else {
 		at86rf230_xmit_start(ctx);
 		at86rf230_xmit_start(ctx);
 	}
 	}
@@ -1373,10 +1380,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
 		return rc;
 		return rc;
 
 
 	irq_type = irq_get_trigger_type(lp->spi->irq);
 	irq_type = irq_get_trigger_type(lp->spi->irq);
-	if (irq_type == IRQ_TYPE_EDGE_RISING ||
-	    irq_type == IRQ_TYPE_EDGE_FALLING)
-		dev_warn(&lp->spi->dev,
-			 "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n");
 	if (irq_type == IRQ_TYPE_EDGE_FALLING ||
 	if (irq_type == IRQ_TYPE_EDGE_FALLING ||
 	    irq_type == IRQ_TYPE_LEVEL_LOW)
 	    irq_type == IRQ_TYPE_LEVEL_LOW)
 		irq_pol = IRQ_ACTIVE_LOW;
 		irq_pol = IRQ_ACTIVE_LOW;
@@ -1602,43 +1605,6 @@ not_supp:
 	return rc;
 	return rc;
 }
 }
 
 
-static void
-at86rf230_setup_spi_messages(struct at86rf230_local *lp)
-{
-	lp->state.lp = lp;
-	lp->state.irq = lp->spi->irq;
-	spi_message_init(&lp->state.msg);
-	lp->state.msg.context = &lp->state;
-	lp->state.trx.len = 2;
-	lp->state.trx.tx_buf = lp->state.buf;
-	lp->state.trx.rx_buf = lp->state.buf;
-	spi_message_add_tail(&lp->state.trx, &lp->state.msg);
-	hrtimer_init(&lp->state.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	lp->state.timer.function = at86rf230_async_state_timer;
-
-	lp->irq.lp = lp;
-	lp->irq.irq = lp->spi->irq;
-	spi_message_init(&lp->irq.msg);
-	lp->irq.msg.context = &lp->irq;
-	lp->irq.trx.len = 2;
-	lp->irq.trx.tx_buf = lp->irq.buf;
-	lp->irq.trx.rx_buf = lp->irq.buf;
-	spi_message_add_tail(&lp->irq.trx, &lp->irq.msg);
-	hrtimer_init(&lp->irq.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	lp->irq.timer.function = at86rf230_async_state_timer;
-
-	lp->tx.lp = lp;
-	lp->tx.irq = lp->spi->irq;
-	spi_message_init(&lp->tx.msg);
-	lp->tx.msg.context = &lp->tx;
-	lp->tx.trx.len = 2;
-	lp->tx.trx.tx_buf = lp->tx.buf;
-	lp->tx.trx.rx_buf = lp->tx.buf;
-	spi_message_add_tail(&lp->tx.trx, &lp->tx.msg);
-	hrtimer_init(&lp->tx.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
-	lp->tx.timer.function = at86rf230_async_state_timer;
-}
-
 #ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS
 #ifdef CONFIG_IEEE802154_AT86RF230_DEBUGFS
 static struct dentry *at86rf230_debugfs_root;
 static struct dentry *at86rf230_debugfs_root;
 
 
@@ -1760,7 +1726,8 @@ static int at86rf230_probe(struct spi_device *spi)
 		goto free_dev;
 		goto free_dev;
 	}
 	}
 
 
-	at86rf230_setup_spi_messages(lp);
+	at86rf230_setup_spi_messages(lp, &lp->state);
+	at86rf230_setup_spi_messages(lp, &lp->tx);
 
 
 	rc = at86rf230_detect_device(lp);
 	rc = at86rf230_detect_device(lp);
 	if (rc < 0)
 	if (rc < 0)

+ 997 - 427
drivers/net/ieee802154/mrf24j40.c

@@ -18,51 +18,172 @@
 #include <linux/spi/spi.h>
 #include <linux/spi/spi.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <linux/ieee802154.h>
 #include <linux/ieee802154.h>
+#include <linux/irq.h>
 #include <net/cfg802154.h>
 #include <net/cfg802154.h>
 #include <net/mac802154.h>
 #include <net/mac802154.h>
 
 
 /* MRF24J40 Short Address Registers */
 /* MRF24J40 Short Address Registers */
-#define REG_RXMCR    0x00  /* Receive MAC control */
-#define REG_PANIDL   0x01  /* PAN ID (low) */
-#define REG_PANIDH   0x02  /* PAN ID (high) */
-#define REG_SADRL    0x03  /* Short address (low) */
-#define REG_SADRH    0x04  /* Short address (high) */
-#define REG_EADR0    0x05  /* Long address (low) (high is EADR7) */
-#define REG_TXMCR    0x11  /* Transmit MAC control */
-#define REG_PACON0   0x16  /* Power Amplifier Control */
-#define REG_PACON1   0x17  /* Power Amplifier Control */
-#define REG_PACON2   0x18  /* Power Amplifier Control */
-#define REG_TXNCON   0x1B  /* Transmit Normal FIFO Control */
-#define REG_TXSTAT   0x24  /* TX MAC Status Register */
-#define REG_SOFTRST  0x2A  /* Soft Reset */
-#define REG_TXSTBL   0x2E  /* TX Stabilization */
-#define REG_INTSTAT  0x31  /* Interrupt Status */
-#define REG_INTCON   0x32  /* Interrupt Control */
-#define REG_GPIO     0x33  /* GPIO */
-#define REG_TRISGPIO 0x34  /* GPIO direction */
-#define REG_RFCTL    0x36  /* RF Control Mode Register */
-#define REG_BBREG1   0x39  /* Baseband Registers */
-#define REG_BBREG2   0x3A  /* */
-#define REG_BBREG6   0x3E  /* */
-#define REG_CCAEDTH  0x3F  /* Energy Detection Threshold */
+#define REG_RXMCR	0x00  /* Receive MAC control */
+#define BIT_PROMI	BIT(0)
+#define BIT_ERRPKT	BIT(1)
+#define BIT_NOACKRSP	BIT(5)
+#define BIT_PANCOORD	BIT(3)
+
+#define REG_PANIDL	0x01  /* PAN ID (low) */
+#define REG_PANIDH	0x02  /* PAN ID (high) */
+#define REG_SADRL	0x03  /* Short address (low) */
+#define REG_SADRH	0x04  /* Short address (high) */
+#define REG_EADR0	0x05  /* Long address (low) (high is EADR7) */
+#define REG_EADR1	0x06
+#define REG_EADR2	0x07
+#define REG_EADR3	0x08
+#define REG_EADR4	0x09
+#define REG_EADR5	0x0A
+#define REG_EADR6	0x0B
+#define REG_EADR7	0x0C
+#define REG_RXFLUSH	0x0D
+#define REG_ORDER	0x10
+#define REG_TXMCR	0x11  /* Transmit MAC control */
+#define TXMCR_MIN_BE_SHIFT		3
+#define TXMCR_MIN_BE_MASK		0x18
+#define TXMCR_CSMA_RETRIES_SHIFT	0
+#define TXMCR_CSMA_RETRIES_MASK		0x07
+
+#define REG_ACKTMOUT	0x12
+#define REG_ESLOTG1	0x13
+#define REG_SYMTICKL	0x14
+#define REG_SYMTICKH	0x15
+#define REG_PACON0	0x16  /* Power Amplifier Control */
+#define REG_PACON1	0x17  /* Power Amplifier Control */
+#define REG_PACON2	0x18  /* Power Amplifier Control */
+#define REG_TXBCON0	0x1A
+#define REG_TXNCON	0x1B  /* Transmit Normal FIFO Control */
+#define BIT_TXNTRIG	BIT(0)
+#define BIT_TXNACKREQ	BIT(2)
+
+#define REG_TXG1CON	0x1C
+#define REG_TXG2CON	0x1D
+#define REG_ESLOTG23	0x1E
+#define REG_ESLOTG45	0x1F
+#define REG_ESLOTG67	0x20
+#define REG_TXPEND	0x21
+#define REG_WAKECON	0x22
+#define REG_FROMOFFSET	0x23
+#define REG_TXSTAT	0x24  /* TX MAC Status Register */
+#define REG_TXBCON1	0x25
+#define REG_GATECLK	0x26
+#define REG_TXTIME	0x27
+#define REG_HSYMTMRL	0x28
+#define REG_HSYMTMRH	0x29
+#define REG_SOFTRST	0x2A  /* Soft Reset */
+#define REG_SECCON0	0x2C
+#define REG_SECCON1	0x2D
+#define REG_TXSTBL	0x2E  /* TX Stabilization */
+#define REG_RXSR	0x30
+#define REG_INTSTAT	0x31  /* Interrupt Status */
+#define BIT_TXNIF	BIT(0)
+#define BIT_RXIF	BIT(3)
+
+#define REG_INTCON	0x32  /* Interrupt Control */
+#define BIT_TXNIE	BIT(0)
+#define BIT_RXIE	BIT(3)
+
+#define REG_GPIO	0x33  /* GPIO */
+#define REG_TRISGPIO	0x34  /* GPIO direction */
+#define REG_SLPACK	0x35
+#define REG_RFCTL	0x36  /* RF Control Mode Register */
+#define BIT_RFRST	BIT(2)
+
+#define REG_SECCR2	0x37
+#define REG_BBREG0	0x38
+#define REG_BBREG1	0x39  /* Baseband Registers */
+#define BIT_RXDECINV	BIT(2)
+
+#define REG_BBREG2	0x3A  /* */
+#define BBREG2_CCA_MODE_SHIFT	6
+#define BBREG2_CCA_MODE_MASK	0xc0
+
+#define REG_BBREG3	0x3B
+#define REG_BBREG4	0x3C
+#define REG_BBREG6	0x3E  /* */
+#define REG_CCAEDTH	0x3F  /* Energy Detection Threshold */
 
 
 /* MRF24J40 Long Address Registers */
 /* MRF24J40 Long Address Registers */
-#define REG_RFCON0     0x200  /* RF Control Registers */
-#define REG_RFCON1     0x201
-#define REG_RFCON2     0x202
-#define REG_RFCON3     0x203
-#define REG_RFCON5     0x205
-#define REG_RFCON6     0x206
-#define REG_RFCON7     0x207
-#define REG_RFCON8     0x208
-#define REG_RSSI       0x210
-#define REG_SLPCON0    0x211  /* Sleep Clock Control Registers */
-#define REG_SLPCON1    0x220
-#define REG_WAKETIMEL  0x222  /* Wake-up Time Match Value Low */
-#define REG_WAKETIMEH  0x223  /* Wake-up Time Match Value High */
-#define REG_TESTMODE   0x22F  /* Test mode */
-#define REG_RX_FIFO    0x300  /* Receive FIFO */
+#define REG_RFCON0	0x200  /* RF Control Registers */
+#define RFCON0_CH_SHIFT	4
+#define RFCON0_CH_MASK	0xf0
+#define RFOPT_RECOMMEND	3
+
+#define REG_RFCON1	0x201
+#define REG_RFCON2	0x202
+#define REG_RFCON3	0x203
+
+#define TXPWRL_MASK	0xc0
+#define TXPWRL_SHIFT	6
+#define TXPWRL_30	0x3
+#define TXPWRL_20	0x2
+#define TXPWRL_10	0x1
+#define TXPWRL_0	0x0
+
+#define TXPWRS_MASK	0x38
+#define TXPWRS_SHIFT	3
+#define TXPWRS_6_3	0x7
+#define TXPWRS_4_9	0x6
+#define TXPWRS_3_7	0x5
+#define TXPWRS_2_8	0x4
+#define TXPWRS_1_9	0x3
+#define TXPWRS_1_2	0x2
+#define TXPWRS_0_5	0x1
+#define TXPWRS_0	0x0
+
+#define REG_RFCON5	0x205
+#define REG_RFCON6	0x206
+#define REG_RFCON7	0x207
+#define REG_RFCON8	0x208
+#define REG_SLPCAL0	0x209
+#define REG_SLPCAL1	0x20A
+#define REG_SLPCAL2	0x20B
+#define REG_RFSTATE	0x20F
+#define REG_RSSI	0x210
+#define REG_SLPCON0	0x211  /* Sleep Clock Control Registers */
+#define BIT_INTEDGE	BIT(1)
+
+#define REG_SLPCON1	0x220
+#define REG_WAKETIMEL	0x222  /* Wake-up Time Match Value Low */
+#define REG_WAKETIMEH	0x223  /* Wake-up Time Match Value High */
+#define REG_REMCNTL	0x224
+#define REG_REMCNTH	0x225
+#define REG_MAINCNT0	0x226
+#define REG_MAINCNT1	0x227
+#define REG_MAINCNT2	0x228
+#define REG_MAINCNT3	0x229
+#define REG_TESTMODE	0x22F  /* Test mode */
+#define REG_ASSOEAR0	0x230
+#define REG_ASSOEAR1	0x231
+#define REG_ASSOEAR2	0x232
+#define REG_ASSOEAR3	0x233
+#define REG_ASSOEAR4	0x234
+#define REG_ASSOEAR5	0x235
+#define REG_ASSOEAR6	0x236
+#define REG_ASSOEAR7	0x237
+#define REG_ASSOSAR0	0x238
+#define REG_ASSOSAR1	0x239
+#define REG_UNONCE0	0x240
+#define REG_UNONCE1	0x241
+#define REG_UNONCE2	0x242
+#define REG_UNONCE3	0x243
+#define REG_UNONCE4	0x244
+#define REG_UNONCE5	0x245
+#define REG_UNONCE6	0x246
+#define REG_UNONCE7	0x247
+#define REG_UNONCE8	0x248
+#define REG_UNONCE9	0x249
+#define REG_UNONCE10	0x24A
+#define REG_UNONCE11	0x24B
+#define REG_UNONCE12	0x24C
+#define REG_RX_FIFO	0x300  /* Receive FIFO */
 
 
 /* Device configuration: Only channels 11-26 on page 0 are supported. */
 /* Device configuration: Only channels 11-26 on page 0 are supported. */
 #define MRF24J40_CHAN_MIN 11
 #define MRF24J40_CHAN_MIN 11
@@ -81,11 +202,52 @@ struct mrf24j40 {
 	struct spi_device *spi;
 	struct spi_device *spi;
 	struct ieee802154_hw *hw;
 	struct ieee802154_hw *hw;
 
 
-	struct mutex buffer_mutex; /* only used to protect buf */
-	struct completion tx_complete;
-	u8 *buf; /* 3 bytes. Used for SPI single-register transfers. */
+	struct regmap *regmap_short;
+	struct regmap *regmap_long;
+
+	/* for writing txfifo */
+	struct spi_message tx_msg;
+	u8 tx_hdr_buf[2];
+	struct spi_transfer tx_hdr_trx;
+	u8 tx_len_buf[2];
+	struct spi_transfer tx_len_trx;
+	struct spi_transfer tx_buf_trx;
+	struct sk_buff *tx_skb;
+
+	/* post transmit message to send frame out  */
+	struct spi_message tx_post_msg;
+	u8 tx_post_buf[2];
+	struct spi_transfer tx_post_trx;
+
+	/* for protect/unprotect/read length rxfifo */
+	struct spi_message rx_msg;
+	u8 rx_buf[3];
+	struct spi_transfer rx_trx;
+
+	/* receive handling */
+	struct spi_message rx_buf_msg;
+	u8 rx_addr_buf[2];
+	struct spi_transfer rx_addr_trx;
+	u8 rx_lqi_buf[2];
+	struct spi_transfer rx_lqi_trx;
+	u8 rx_fifo_buf[RX_FIFO_SIZE];
+	struct spi_transfer rx_fifo_buf_trx;
+
+	/* isr handling for reading intstat */
+	struct spi_message irq_msg;
+	u8 irq_buf[2];
+	struct spi_transfer irq_trx;
 };
 };
 
 
+/* regmap information for short address register access */
+#define MRF24J40_SHORT_WRITE	0x01
+#define MRF24J40_SHORT_READ	0x00
+#define MRF24J40_SHORT_NUMREGS	0x3F
+
+/* regmap information for long address register access */
+#define MRF24J40_LONG_ACCESS	0x80
+#define MRF24J40_LONG_NUMREGS	0x38F
+
 /* Read/Write SPI Commands for Short and Long Address registers. */
 /* Read/Write SPI Commands for Short and Long Address registers. */
 #define MRF24J40_READSHORT(reg) ((reg) << 1)
 #define MRF24J40_READSHORT(reg) ((reg) << 1)
 #define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1)
 #define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1)
@@ -97,118 +259,304 @@ struct mrf24j40 {
 
 
 #define printdev(X) (&X->spi->dev)
 #define printdev(X) (&X->spi->dev)
 
 
-static int write_short_reg(struct mrf24j40 *devrec, u8 reg, u8 value)
+static bool
+mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg)
 {
 {
-	int ret;
-	struct spi_message msg;
-	struct spi_transfer xfer = {
-		.len = 2,
-		.tx_buf = devrec->buf,
-		.rx_buf = devrec->buf,
-	};
+	switch (reg) {
+	case REG_RXMCR:
+	case REG_PANIDL:
+	case REG_PANIDH:
+	case REG_SADRL:
+	case REG_SADRH:
+	case REG_EADR0:
+	case REG_EADR1:
+	case REG_EADR2:
+	case REG_EADR3:
+	case REG_EADR4:
+	case REG_EADR5:
+	case REG_EADR6:
+	case REG_EADR7:
+	case REG_RXFLUSH:
+	case REG_ORDER:
+	case REG_TXMCR:
+	case REG_ACKTMOUT:
+	case REG_ESLOTG1:
+	case REG_SYMTICKL:
+	case REG_SYMTICKH:
+	case REG_PACON0:
+	case REG_PACON1:
+	case REG_PACON2:
+	case REG_TXBCON0:
+	case REG_TXNCON:
+	case REG_TXG1CON:
+	case REG_TXG2CON:
+	case REG_ESLOTG23:
+	case REG_ESLOTG45:
+	case REG_ESLOTG67:
+	case REG_TXPEND:
+	case REG_WAKECON:
+	case REG_FROMOFFSET:
+	case REG_TXBCON1:
+	case REG_GATECLK:
+	case REG_TXTIME:
+	case REG_HSYMTMRL:
+	case REG_HSYMTMRH:
+	case REG_SOFTRST:
+	case REG_SECCON0:
+	case REG_SECCON1:
+	case REG_TXSTBL:
+	case REG_RXSR:
+	case REG_INTCON:
+	case REG_TRISGPIO:
+	case REG_GPIO:
+	case REG_RFCTL:
+	case REG_SLPACK:
+	case REG_BBREG0:
+	case REG_BBREG1:
+	case REG_BBREG2:
+	case REG_BBREG3:
+	case REG_BBREG4:
+	case REG_BBREG6:
+	case REG_CCAEDTH:
+		return true;
+	default:
+		return false;
+	}
+}
 
 
-	spi_message_init(&msg);
-	spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_short_reg_readable(struct device *dev, unsigned int reg)
+{
+	bool rc;
+
+	/* all writeable are also readable */
+	rc = mrf24j40_short_reg_writeable(dev, reg);
+	if (rc)
+		return rc;
+
+	/* readonly regs */
+	switch (reg) {
+	case REG_TXSTAT:
+	case REG_INTSTAT:
+		return true;
+	default:
+		return false;
+	}
+}
 
 
-	mutex_lock(&devrec->buffer_mutex);
-	devrec->buf[0] = MRF24J40_WRITESHORT(reg);
-	devrec->buf[1] = value;
+static bool
+mrf24j40_short_reg_volatile(struct device *dev, unsigned int reg)
+{
+	/* can be changed during runtime */
+	switch (reg) {
+	case REG_TXSTAT:
+	case REG_INTSTAT:
+	case REG_RXFLUSH:
+	case REG_TXNCON:
+	case REG_SOFTRST:
+	case REG_RFCTL:
+	case REG_TXBCON0:
+	case REG_TXG1CON:
+	case REG_TXG2CON:
+	case REG_TXBCON1:
+	case REG_SECCON0:
+	case REG_RXSR:
+	case REG_SLPACK:
+	case REG_SECCR2:
+	case REG_BBREG6:
+	/* use them in spi_async and regmap so it's volatile */
+	case REG_BBREG1:
+		return true;
+	default:
+		return false;
+	}
+}
 
 
-	ret = spi_sync(devrec->spi, &msg);
-	if (ret)
-		dev_err(printdev(devrec),
-			"SPI write Failed for short register 0x%hhx\n", reg);
+static bool
+mrf24j40_short_reg_precious(struct device *dev, unsigned int reg)
+{
+	/* don't clear irq line on read */
+	switch (reg) {
+	case REG_INTSTAT:
+		return true;
+	default:
+		return false;
+	}
+}
 
 
-	mutex_unlock(&devrec->buffer_mutex);
-	return ret;
+static const struct regmap_config mrf24j40_short_regmap = {
+	.name = "mrf24j40_short",
+	.reg_bits = 7,
+	.val_bits = 8,
+	.pad_bits = 1,
+	.write_flag_mask = MRF24J40_SHORT_WRITE,
+	.read_flag_mask = MRF24J40_SHORT_READ,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = MRF24J40_SHORT_NUMREGS,
+	.writeable_reg = mrf24j40_short_reg_writeable,
+	.readable_reg = mrf24j40_short_reg_readable,
+	.volatile_reg = mrf24j40_short_reg_volatile,
+	.precious_reg = mrf24j40_short_reg_precious,
+};
+
+static bool
+mrf24j40_long_reg_writeable(struct device *dev, unsigned int reg)
+{
+	switch (reg) {
+	case REG_RFCON0:
+	case REG_RFCON1:
+	case REG_RFCON2:
+	case REG_RFCON3:
+	case REG_RFCON5:
+	case REG_RFCON6:
+	case REG_RFCON7:
+	case REG_RFCON8:
+	case REG_SLPCAL2:
+	case REG_SLPCON0:
+	case REG_SLPCON1:
+	case REG_WAKETIMEL:
+	case REG_WAKETIMEH:
+	case REG_REMCNTL:
+	case REG_REMCNTH:
+	case REG_MAINCNT0:
+	case REG_MAINCNT1:
+	case REG_MAINCNT2:
+	case REG_MAINCNT3:
+	case REG_TESTMODE:
+	case REG_ASSOEAR0:
+	case REG_ASSOEAR1:
+	case REG_ASSOEAR2:
+	case REG_ASSOEAR3:
+	case REG_ASSOEAR4:
+	case REG_ASSOEAR5:
+	case REG_ASSOEAR6:
+	case REG_ASSOEAR7:
+	case REG_ASSOSAR0:
+	case REG_ASSOSAR1:
+	case REG_UNONCE0:
+	case REG_UNONCE1:
+	case REG_UNONCE2:
+	case REG_UNONCE3:
+	case REG_UNONCE4:
+	case REG_UNONCE5:
+	case REG_UNONCE6:
+	case REG_UNONCE7:
+	case REG_UNONCE8:
+	case REG_UNONCE9:
+	case REG_UNONCE10:
+	case REG_UNONCE11:
+	case REG_UNONCE12:
+		return true;
+	default:
+		return false;
+	}
 }
 }
 
 
-static int read_short_reg(struct mrf24j40 *devrec, u8 reg, u8 *val)
+static bool
+mrf24j40_long_reg_readable(struct device *dev, unsigned int reg)
 {
 {
-	int ret = -1;
-	struct spi_message msg;
-	struct spi_transfer xfer = {
-		.len = 2,
-		.tx_buf = devrec->buf,
-		.rx_buf = devrec->buf,
-	};
+	bool rc;
+
+	/* all writeable are also readable */
+	rc = mrf24j40_long_reg_writeable(dev, reg);
+	if (rc)
+		return rc;
+
+	/* readonly regs */
+	switch (reg) {
+	case REG_SLPCAL0:
+	case REG_SLPCAL1:
+	case REG_RFSTATE:
+	case REG_RSSI:
+		return true;
+	default:
+		return false;
+	}
+}
 
 
-	spi_message_init(&msg);
-	spi_message_add_tail(&xfer, &msg);
+static bool
+mrf24j40_long_reg_volatile(struct device *dev, unsigned int reg)
+{
+	/* can be changed during runtime */
+	switch (reg) {
+	case REG_SLPCAL0:
+	case REG_SLPCAL1:
+	case REG_SLPCAL2:
+	case REG_RFSTATE:
+	case REG_RSSI:
+	case REG_MAINCNT3:
+		return true;
+	default:
+		return false;
+	}
+}
 
 
-	mutex_lock(&devrec->buffer_mutex);
-	devrec->buf[0] = MRF24J40_READSHORT(reg);
-	devrec->buf[1] = 0;
+static const struct regmap_config mrf24j40_long_regmap = {
+	.name = "mrf24j40_long",
+	.reg_bits = 11,
+	.val_bits = 8,
+	.pad_bits = 5,
+	.write_flag_mask = MRF24J40_LONG_ACCESS,
+	.read_flag_mask = MRF24J40_LONG_ACCESS,
+	.cache_type = REGCACHE_RBTREE,
+	.max_register = MRF24J40_LONG_NUMREGS,
+	.writeable_reg = mrf24j40_long_reg_writeable,
+	.readable_reg = mrf24j40_long_reg_readable,
+	.volatile_reg = mrf24j40_long_reg_volatile,
+};
 
 
-	ret = spi_sync(devrec->spi, &msg);
-	if (ret)
-		dev_err(printdev(devrec),
-			"SPI read Failed for short register 0x%hhx\n", reg);
-	else
-		*val = devrec->buf[1];
+static int mrf24j40_long_regmap_write(void *context, const void *data,
+				      size_t count)
+{
+	struct spi_device *spi = context;
+	u8 buf[3];
 
 
-	mutex_unlock(&devrec->buffer_mutex);
-	return ret;
+	if (count > 3)
+		return -EINVAL;
+
+	/* regmap supports read/write mask only in frist byte
+	 * long write access need to set the 12th bit, so we
+	 * make special handling for write.
+	 */
+	memcpy(buf, data, count);
+	buf[1] |= (1 << 4);
+
+	return spi_write(spi, buf, count);
 }
 }
 
 
-static int read_long_reg(struct mrf24j40 *devrec, u16 reg, u8 *value)
+static int
+mrf24j40_long_regmap_read(void *context, const void *reg, size_t reg_size,
+			  void *val, size_t val_size)
 {
 {
-	int ret;
-	u16 cmd;
-	struct spi_message msg;
-	struct spi_transfer xfer = {
-		.len = 3,
-		.tx_buf = devrec->buf,
-		.rx_buf = devrec->buf,
-	};
-
-	spi_message_init(&msg);
-	spi_message_add_tail(&xfer, &msg);
-
-	cmd = MRF24J40_READLONG(reg);
-	mutex_lock(&devrec->buffer_mutex);
-	devrec->buf[0] = cmd >> 8 & 0xff;
-	devrec->buf[1] = cmd & 0xff;
-	devrec->buf[2] = 0;
-
-	ret = spi_sync(devrec->spi, &msg);
-	if (ret)
-		dev_err(printdev(devrec),
-			"SPI read Failed for long register 0x%hx\n", reg);
-	else
-		*value = devrec->buf[2];
+	struct spi_device *spi = context;
 
 
-	mutex_unlock(&devrec->buffer_mutex);
-	return ret;
+	return spi_write_then_read(spi, reg, reg_size, val, val_size);
 }
 }
 
 
-static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
+static const struct regmap_bus mrf24j40_long_regmap_bus = {
+	.write = mrf24j40_long_regmap_write,
+	.read = mrf24j40_long_regmap_read,
+	.reg_format_endian_default = REGMAP_ENDIAN_BIG,
+	.val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void write_tx_buf_complete(void *context)
 {
 {
+	struct mrf24j40 *devrec = context;
+	__le16 fc = ieee802154_get_fc_from_skb(devrec->tx_skb);
+	u8 val = BIT_TXNTRIG;
 	int ret;
 	int ret;
-	u16 cmd;
-	struct spi_message msg;
-	struct spi_transfer xfer = {
-		.len = 3,
-		.tx_buf = devrec->buf,
-		.rx_buf = devrec->buf,
-	};
 
 
-	spi_message_init(&msg);
-	spi_message_add_tail(&xfer, &msg);
+	if (ieee802154_is_ackreq(fc))
+		val |= BIT_TXNACKREQ;
 
 
-	cmd = MRF24J40_WRITELONG(reg);
-	mutex_lock(&devrec->buffer_mutex);
-	devrec->buf[0] = cmd >> 8 & 0xff;
-	devrec->buf[1] = cmd & 0xff;
-	devrec->buf[2] = val;
+	devrec->tx_post_msg.complete = NULL;
+	devrec->tx_post_buf[0] = MRF24J40_WRITESHORT(REG_TXNCON);
+	devrec->tx_post_buf[1] = val;
 
 
-	ret = spi_sync(devrec->spi, &msg);
+	ret = spi_async(devrec->spi, &devrec->tx_post_msg);
 	if (ret)
 	if (ret)
-		dev_err(printdev(devrec),
-			"SPI write Failed for long register 0x%hx\n", reg);
-
-	mutex_unlock(&devrec->buffer_mutex);
-	return ret;
+		dev_err(printdev(devrec), "SPI write Failed for transmit buf\n");
 }
 }
 
 
 /* This function relies on an undocumented write method. Once a write command
 /* This function relies on an undocumented write method. Once a write command
@@ -217,22 +565,8 @@ static int write_long_reg(struct mrf24j40 *devrec, u16 reg, u8 val)
 static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
 static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
 			const u8 *data, size_t length)
 			const u8 *data, size_t length)
 {
 {
-	int ret;
 	u16 cmd;
 	u16 cmd;
-	u8 lengths[2];
-	struct spi_message msg;
-	struct spi_transfer addr_xfer = {
-		.len = 2,
-		.tx_buf = devrec->buf,
-	};
-	struct spi_transfer lengths_xfer = {
-		.len = 2,
-		.tx_buf = &lengths, /* TODO: Is DMA really required for SPI? */
-	};
-	struct spi_transfer data_xfer = {
-		.len = length,
-		.tx_buf = data,
-	};
+	int ret;
 
 
 	/* Range check the length. 2 bytes are used for the length fields.*/
 	/* Range check the length. 2 bytes are used for the length fields.*/
 	if (length > TX_FIFO_SIZE-2) {
 	if (length > TX_FIFO_SIZE-2) {
@@ -240,147 +574,29 @@ static int write_tx_buf(struct mrf24j40 *devrec, u16 reg,
 		length = TX_FIFO_SIZE-2;
 		length = TX_FIFO_SIZE-2;
 	}
 	}
 
 
-	spi_message_init(&msg);
-	spi_message_add_tail(&addr_xfer, &msg);
-	spi_message_add_tail(&lengths_xfer, &msg);
-	spi_message_add_tail(&data_xfer, &msg);
-
 	cmd = MRF24J40_WRITELONG(reg);
 	cmd = MRF24J40_WRITELONG(reg);
-	mutex_lock(&devrec->buffer_mutex);
-	devrec->buf[0] = cmd >> 8 & 0xff;
-	devrec->buf[1] = cmd & 0xff;
-	lengths[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
-	lengths[1] = length; /* Total length */
-
-	ret = spi_sync(devrec->spi, &msg);
+	devrec->tx_hdr_buf[0] = cmd >> 8 & 0xff;
+	devrec->tx_hdr_buf[1] = cmd & 0xff;
+	devrec->tx_len_buf[0] = 0x0; /* Header Length. Set to 0 for now. TODO */
+	devrec->tx_len_buf[1] = length; /* Total length */
+	devrec->tx_buf_trx.tx_buf = data;
+	devrec->tx_buf_trx.len = length;
+
+	ret = spi_async(devrec->spi, &devrec->tx_msg);
 	if (ret)
 	if (ret)
 		dev_err(printdev(devrec), "SPI write Failed for TX buf\n");
 		dev_err(printdev(devrec), "SPI write Failed for TX buf\n");
 
 
-	mutex_unlock(&devrec->buffer_mutex);
-	return ret;
-}
-
-static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
-				u8 *data, u8 *len, u8 *lqi)
-{
-	u8 rx_len;
-	u8 addr[2];
-	u8 lqi_rssi[2];
-	u16 cmd;
-	int ret;
-	struct spi_message msg;
-	struct spi_transfer addr_xfer = {
-		.len = 2,
-		.tx_buf = &addr,
-	};
-	struct spi_transfer data_xfer = {
-		.len = 0x0, /* set below */
-		.rx_buf = data,
-	};
-	struct spi_transfer status_xfer = {
-		.len = 2,
-		.rx_buf = &lqi_rssi,
-	};
-
-	/* Get the length of the data in the RX FIFO. The length in this
-	 * register exclues the 1-byte length field at the beginning. */
-	ret = read_long_reg(devrec, REG_RX_FIFO, &rx_len);
-	if (ret)
-		goto out;
-
-	/* Range check the RX FIFO length, accounting for the one-byte
-	 * length field at the beginning. */
-	if (rx_len > RX_FIFO_SIZE-1) {
-		dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n");
-		rx_len = RX_FIFO_SIZE-1;
-	}
-
-	if (rx_len > *len) {
-		/* Passed in buffer wasn't big enough. Should never happen. */
-		dev_err(printdev(devrec), "Buffer not big enough. Performing short read\n");
-		rx_len = *len;
-	}
-
-	/* Set up the commands to read the data. */
-	cmd = MRF24J40_READLONG(REG_RX_FIFO+1);
-	addr[0] = cmd >> 8 & 0xff;
-	addr[1] = cmd & 0xff;
-	data_xfer.len = rx_len;
-
-	spi_message_init(&msg);
-	spi_message_add_tail(&addr_xfer, &msg);
-	spi_message_add_tail(&data_xfer, &msg);
-	spi_message_add_tail(&status_xfer, &msg);
-
-	ret = spi_sync(devrec->spi, &msg);
-	if (ret) {
-		dev_err(printdev(devrec), "SPI RX Buffer Read Failed.\n");
-		goto out;
-	}
-
-	*lqi = lqi_rssi[0];
-	*len = rx_len;
-
-#ifdef DEBUG
-	print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ",
-		       DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
-	pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
-		 lqi_rssi[0], lqi_rssi[1]);
-#endif
-
-out:
 	return ret;
 	return ret;
 }
 }
 
 
 static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
 static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
 {
 {
 	struct mrf24j40 *devrec = hw->priv;
 	struct mrf24j40 *devrec = hw->priv;
-	u8 val;
-	int ret = 0;
 
 
 	dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len);
 	dev_dbg(printdev(devrec), "tx packet of %d bytes\n", skb->len);
+	devrec->tx_skb = skb;
 
 
-	ret = write_tx_buf(devrec, 0x000, skb->data, skb->len);
-	if (ret)
-		goto err;
-
-	reinit_completion(&devrec->tx_complete);
-
-	/* Set TXNTRIG bit of TXNCON to send packet */
-	ret = read_short_reg(devrec, REG_TXNCON, &val);
-	if (ret)
-		goto err;
-	val |= 0x1;
-	/* Set TXNACKREQ if the ACK bit is set in the packet. */
-	if (skb->data[0] & IEEE802154_FC_ACK_REQ)
-		val |= 0x4;
-	write_short_reg(devrec, REG_TXNCON, val);
-
-	/* Wait for the device to send the TX complete interrupt. */
-	ret = wait_for_completion_interruptible_timeout(
-						&devrec->tx_complete,
-						5 * HZ);
-	if (ret == -ERESTARTSYS)
-		goto err;
-	if (ret == 0) {
-		dev_warn(printdev(devrec), "Timeout waiting for TX interrupt\n");
-		ret = -ETIMEDOUT;
-		goto err;
-	}
-
-	/* Check for send error from the device. */
-	ret = read_short_reg(devrec, REG_TXSTAT, &val);
-	if (ret)
-		goto err;
-	if (val & 0x1) {
-		dev_dbg(printdev(devrec), "Error Sending. Retry count exceeded\n");
-		ret = -ECOMM; /* TODO: Better error code ? */
-	} else
-		dev_dbg(printdev(devrec), "Packet Sent\n");
-
-err:
-
-	return ret;
+	return write_tx_buf(devrec, 0x000, skb->data, skb->len);
 }
 }
 
 
 static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
 static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
@@ -394,33 +610,23 @@ static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
 static int mrf24j40_start(struct ieee802154_hw *hw)
 static int mrf24j40_start(struct ieee802154_hw *hw)
 {
 {
 	struct mrf24j40 *devrec = hw->priv;
 	struct mrf24j40 *devrec = hw->priv;
-	u8 val;
-	int ret;
 
 
 	dev_dbg(printdev(devrec), "start\n");
 	dev_dbg(printdev(devrec), "start\n");
 
 
-	ret = read_short_reg(devrec, REG_INTCON, &val);
-	if (ret)
-		return ret;
-	val &= ~(0x1|0x8); /* Clear TXNIE and RXIE. Enable interrupts */
-	write_short_reg(devrec, REG_INTCON, val);
-
-	return 0;
+	/* Clear TXNIE and RXIE. Enable interrupts */
+	return regmap_update_bits(devrec->regmap_short, REG_INTCON,
+				  BIT_TXNIE | BIT_RXIE, 0);
 }
 }
 
 
 static void mrf24j40_stop(struct ieee802154_hw *hw)
 static void mrf24j40_stop(struct ieee802154_hw *hw)
 {
 {
 	struct mrf24j40 *devrec = hw->priv;
 	struct mrf24j40 *devrec = hw->priv;
-	u8 val;
-	int ret;
 
 
 	dev_dbg(printdev(devrec), "stop\n");
 	dev_dbg(printdev(devrec), "stop\n");
 
 
-	ret = read_short_reg(devrec, REG_INTCON, &val);
-	if (ret)
-		return;
-	val |= 0x1|0x8; /* Set TXNIE and RXIE. Disable Interrupts */
-	write_short_reg(devrec, REG_INTCON, val);
+	/* Set TXNIE and RXIE. Disable Interrupts */
+	regmap_update_bits(devrec->regmap_short, REG_INTCON,
+			   BIT_TXNIE | BIT_TXNIE, BIT_TXNIE | BIT_TXNIE);
 }
 }
 
 
 static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
 static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
@@ -436,21 +642,23 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
 	WARN_ON(channel > MRF24J40_CHAN_MAX);
 	WARN_ON(channel > MRF24J40_CHAN_MAX);
 
 
 	/* Set Channel TODO */
 	/* Set Channel TODO */
-	val = (channel-11) << 4 | 0x03;
-	write_long_reg(devrec, REG_RFCON0, val);
+	val = (channel - 11) << RFCON0_CH_SHIFT | RFOPT_RECOMMEND;
+	ret = regmap_update_bits(devrec->regmap_long, REG_RFCON0,
+				 RFCON0_CH_MASK, val);
+	if (ret)
+		return ret;
 
 
 	/* RF Reset */
 	/* RF Reset */
-	ret = read_short_reg(devrec, REG_RFCTL, &val);
+	ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST,
+				 BIT_RFRST);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
-	val |= 0x04;
-	write_short_reg(devrec, REG_RFCTL, val);
-	val &= ~0x04;
-	write_short_reg(devrec, REG_RFCTL, val);
 
 
-	udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
+	ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST, 0);
+	if (!ret)
+		udelay(SET_CHANNEL_DELAY_US); /* per datasheet */
 
 
-	return 0;
+	return ret;
 }
 }
 
 
 static int mrf24j40_filter(struct ieee802154_hw *hw,
 static int mrf24j40_filter(struct ieee802154_hw *hw,
@@ -468,8 +676,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 		addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff;
 		addrh = le16_to_cpu(filt->short_addr) >> 8 & 0xff;
 		addrl = le16_to_cpu(filt->short_addr) & 0xff;
 		addrl = le16_to_cpu(filt->short_addr) & 0xff;
 
 
-		write_short_reg(devrec, REG_SADRH, addrh);
-		write_short_reg(devrec, REG_SADRL, addrl);
+		regmap_write(devrec->regmap_short, REG_SADRH, addrh);
+		regmap_write(devrec->regmap_short, REG_SADRL, addrl);
 		dev_dbg(printdev(devrec),
 		dev_dbg(printdev(devrec),
 			"Set short addr to %04hx\n", filt->short_addr);
 			"Set short addr to %04hx\n", filt->short_addr);
 	}
 	}
@@ -480,7 +688,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 
 
 		memcpy(addr, &filt->ieee_addr, 8);
 		memcpy(addr, &filt->ieee_addr, 8);
 		for (i = 0; i < 8; i++)
 		for (i = 0; i < 8; i++)
-			write_short_reg(devrec, REG_EADR0 + i, addr[i]);
+			regmap_write(devrec->regmap_short, REG_EADR0 + i,
+				     addr[i]);
 
 
 #ifdef DEBUG
 #ifdef DEBUG
 		pr_debug("Set long addr to: ");
 		pr_debug("Set long addr to: ");
@@ -496,8 +705,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 
 
 		panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff;
 		panidh = le16_to_cpu(filt->pan_id) >> 8 & 0xff;
 		panidl = le16_to_cpu(filt->pan_id) & 0xff;
 		panidl = le16_to_cpu(filt->pan_id) & 0xff;
-		write_short_reg(devrec, REG_PANIDH, panidh);
-		write_short_reg(devrec, REG_PANIDL, panidl);
+		regmap_write(devrec->regmap_short, REG_PANIDH, panidh);
+		regmap_write(devrec->regmap_short, REG_PANIDL, panidl);
 
 
 		dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
 		dev_dbg(printdev(devrec), "Set PANID to %04hx\n", filt->pan_id);
 	}
 	}
@@ -507,14 +716,14 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 		u8 val;
 		u8 val;
 		int ret;
 		int ret;
 
 
-		ret = read_short_reg(devrec, REG_RXMCR, &val);
-		if (ret)
-			return ret;
 		if (filt->pan_coord)
 		if (filt->pan_coord)
-			val |= 0x8;
+			val = BIT_PANCOORD;
 		else
 		else
-			val &= ~0x8;
-		write_short_reg(devrec, REG_RXMCR, val);
+			val = 0;
+		ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+					 BIT_PANCOORD, val);
+		if (ret)
+			return ret;
 
 
 		/* REG_SLOTTED is maintained as default (unslotted/CSMA-CA).
 		/* REG_SLOTTED is maintained as default (unslotted/CSMA-CA).
 		 * REG_ORDER is maintained as default (no beacon/superframe).
 		 * REG_ORDER is maintained as default (no beacon/superframe).
@@ -527,168 +736,392 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
 	return 0;
 	return 0;
 }
 }
 
 
-static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+static void mrf24j40_handle_rx_read_buf_unlock(struct mrf24j40 *devrec)
 {
 {
-	u8 len = RX_FIFO_SIZE;
-	u8 lqi = 0;
-	u8 val;
-	int ret = 0;
-	int ret2;
-	struct sk_buff *skb;
+	int ret;
 
 
-	/* Turn off reception of packets off the air. This prevents the
-	 * device from overwriting the buffer while we're reading it. */
-	ret = read_short_reg(devrec, REG_BBREG1, &val);
+	/* Turn back on reception of packets off the air. */
+	devrec->rx_msg.complete = NULL;
+	devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+	devrec->rx_buf[1] = 0x00; /* CLR RXDECINV */
+	ret = spi_async(devrec->spi, &devrec->rx_msg);
 	if (ret)
 	if (ret)
-		goto out;
-	val |= 4; /* SET RXDECINV */
-	write_short_reg(devrec, REG_BBREG1, val);
+		dev_err(printdev(devrec), "failed to unlock rx buffer\n");
+}
+
+static void mrf24j40_handle_rx_read_buf_complete(void *context)
+{
+	struct mrf24j40 *devrec = context;
+	u8 len = devrec->rx_buf[2];
+	u8 rx_local_buf[RX_FIFO_SIZE];
+	struct sk_buff *skb;
+
+	memcpy(rx_local_buf, devrec->rx_fifo_buf, len);
+	mrf24j40_handle_rx_read_buf_unlock(devrec);
 
 
-	skb = dev_alloc_skb(len);
+	skb = dev_alloc_skb(IEEE802154_MTU);
 	if (!skb) {
 	if (!skb) {
-		ret = -ENOMEM;
-		goto out;
+		dev_err(printdev(devrec), "failed to allocate skb\n");
+		return;
 	}
 	}
 
 
-	ret = mrf24j40_read_rx_buf(devrec, skb_put(skb, len), &len, &lqi);
-	if (ret < 0) {
-		dev_err(printdev(devrec), "Failure reading RX FIFO\n");
-		kfree_skb(skb);
-		ret = -EINVAL;
-		goto out;
+	memcpy(skb_put(skb, len), rx_local_buf, len);
+	ieee802154_rx_irqsafe(devrec->hw, skb, 0);
+
+#ifdef DEBUG
+	 print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ", DUMP_PREFIX_OFFSET, 16, 1,
+			rx_local_buf, len, 0);
+	 pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
+		  devrec->rx_lqi_buf[0], devrec->rx_lqi_buf[1]);
+#endif
+}
+
+static void mrf24j40_handle_rx_read_buf(void *context)
+{
+	struct mrf24j40 *devrec = context;
+	u16 cmd;
+	int ret;
+
+	/* if length is invalid read the full MTU */
+	if (!ieee802154_is_valid_psdu_len(devrec->rx_buf[2]))
+		devrec->rx_buf[2] = IEEE802154_MTU;
+
+	cmd = MRF24J40_READLONG(REG_RX_FIFO + 1);
+	devrec->rx_addr_buf[0] = cmd >> 8 & 0xff;
+	devrec->rx_addr_buf[1] = cmd & 0xff;
+	devrec->rx_fifo_buf_trx.len = devrec->rx_buf[2];
+	ret = spi_async(devrec->spi, &devrec->rx_buf_msg);
+	if (ret) {
+		dev_err(printdev(devrec), "failed to read rx buffer\n");
+		mrf24j40_handle_rx_read_buf_unlock(devrec);
 	}
 	}
+}
 
 
-	/* Cut off the checksum */
-	skb_trim(skb, len-2);
+static void mrf24j40_handle_rx_read_len(void *context)
+{
+	struct mrf24j40 *devrec = context;
+	u16 cmd;
+	int ret;
 
 
-	/* TODO: Other drivers call ieee20154_rx_irqsafe() here (eg: cc2040,
-	 * also from a workqueue).  I think irqsafe is not necessary here.
-	 * Can someone confirm? */
-	ieee802154_rx_irqsafe(devrec->hw, skb, lqi);
+	/* read the length of received frame */
+	devrec->rx_msg.complete = mrf24j40_handle_rx_read_buf;
+	devrec->rx_trx.len = 3;
+	cmd = MRF24J40_READLONG(REG_RX_FIFO);
+	devrec->rx_buf[0] = cmd >> 8 & 0xff;
+	devrec->rx_buf[1] = cmd & 0xff;
 
 
-	dev_dbg(printdev(devrec), "RX Handled\n");
+	ret = spi_async(devrec->spi, &devrec->rx_msg);
+	if (ret) {
+		dev_err(printdev(devrec), "failed to read rx buffer length\n");
+		mrf24j40_handle_rx_read_buf_unlock(devrec);
+	}
+}
 
 
-out:
-	/* Turn back on reception of packets off the air. */
-	ret2 = read_short_reg(devrec, REG_BBREG1, &val);
-	if (ret2)
-		return ret2;
-	val &= ~0x4; /* Clear RXDECINV */
-	write_short_reg(devrec, REG_BBREG1, val);
+static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
+{
+	/* Turn off reception of packets off the air. This prevents the
+	 * device from overwriting the buffer while we're reading it.
+	 */
+	devrec->rx_msg.complete = mrf24j40_handle_rx_read_len;
+	devrec->rx_trx.len = 2;
+	devrec->rx_buf[0] = MRF24J40_WRITESHORT(REG_BBREG1);
+	devrec->rx_buf[1] = BIT_RXDECINV; /* SET RXDECINV */
+
+	return spi_async(devrec->spi, &devrec->rx_msg);
+}
+
+static int
+mrf24j40_csma_params(struct ieee802154_hw *hw, u8 min_be, u8 max_be,
+		     u8 retries)
+{
+	struct mrf24j40 *devrec = hw->priv;
+	u8 val;
+
+	/* min_be */
+	val = min_be << TXMCR_MIN_BE_SHIFT;
+	/* csma backoffs */
+	val |= retries << TXMCR_CSMA_RETRIES_SHIFT;
+
+	return regmap_update_bits(devrec->regmap_short, REG_TXMCR,
+				  TXMCR_MIN_BE_MASK | TXMCR_CSMA_RETRIES_MASK,
+				  val);
+}
+
+static int mrf24j40_set_cca_mode(struct ieee802154_hw *hw,
+				 const struct wpan_phy_cca *cca)
+{
+	struct mrf24j40 *devrec = hw->priv;
+	u8 val;
+
+	/* mapping 802.15.4 to driver spec */
+	switch (cca->mode) {
+	case NL802154_CCA_ENERGY:
+		val = 2;
+		break;
+	case NL802154_CCA_CARRIER:
+		val = 1;
+		break;
+	case NL802154_CCA_ENERGY_CARRIER:
+		switch (cca->opt) {
+		case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
+			val = 3;
+			break;
+		default:
+			return -EINVAL;
+		}
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(devrec->regmap_short, REG_BBREG2,
+				  BBREG2_CCA_MODE_MASK,
+				  val << BBREG2_CCA_MODE_SHIFT);
+}
+
+/* array for representing ed levels */
+static const s32 mrf24j40_ed_levels[] = {
+	-9000, -8900, -8800, -8700, -8600, -8500, -8400, -8300, -8200, -8100,
+	-8000, -7900, -7800, -7700, -7600, -7500, -7400, -7300, -7200, -7100,
+	-7000, -6900, -6800, -6700, -6600, -6500, -6400, -6300, -6200, -6100,
+	-6000, -5900, -5800, -5700, -5600, -5500, -5400, -5300, -5200, -5100,
+	-5000, -4900, -4800, -4700, -4600, -4500, -4400, -4300, -4200, -4100,
+	-4000, -3900, -3800, -3700, -3600, -3500
+};
+
+/* map ed levels to register value */
+static const s32 mrf24j40_ed_levels_map[][2] = {
+	{ -9000, 0 }, { -8900, 1 }, { -8800, 2 }, { -8700, 5 }, { -8600, 9 },
+	{ -8500, 13 }, { -8400, 18 }, { -8300, 23 }, { -8200, 27 },
+	{ -8100, 32 }, { -8000, 37 }, { -7900, 43 }, { -7800, 48 },
+	{ -7700, 53 }, { -7600, 58 }, { -7500, 63 }, { -7400, 68 },
+	{ -7300, 73 }, { -7200, 78 }, { -7100, 83 }, { -7000, 89 },
+	{ -6900, 95 }, { -6800, 100 }, { -6700, 107 }, { -6600, 111 },
+	{ -6500, 117 }, { -6400, 121 }, { -6300, 125 }, { -6200, 129 },
+	{ -6100, 133 },	{ -6000, 138 }, { -5900, 143 }, { -5800, 148 },
+	{ -5700, 153 }, { -5600, 159 },	{ -5500, 165 }, { -5400, 170 },
+	{ -5300, 176 }, { -5200, 183 }, { -5100, 188 }, { -5000, 193 },
+	{ -4900, 198 }, { -4800, 203 }, { -4700, 207 }, { -4600, 212 },
+	{ -4500, 216 }, { -4400, 221 }, { -4300, 225 }, { -4200, 228 },
+	{ -4100, 233 }, { -4000, 239 }, { -3900, 245 }, { -3800, 250 },
+	{ -3700, 253 }, { -3600, 254 }, { -3500, 255 },
+};
+
+static int mrf24j40_set_cca_ed_level(struct ieee802154_hw *hw, s32 mbm)
+{
+	struct mrf24j40 *devrec = hw->priv;
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(mrf24j40_ed_levels_map); i++) {
+		if (mrf24j40_ed_levels_map[i][0] == mbm)
+			return regmap_write(devrec->regmap_short, REG_CCAEDTH,
+					    mrf24j40_ed_levels_map[i][1]);
+	}
+
+	return -EINVAL;
+}
+
+static const s32 mrf24j40ma_powers[] = {
+	0, -50, -120, -190, -280, -370, -490, -630, -1000, -1050, -1120, -1190,
+	-1280, -1370, -1490, -1630, -2000, -2050, -2120, -2190, -2280, -2370,
+	-2490, -2630, -3000, -3050, -3120, -3190, -3280, -3370, -3490, -3630,
+};
+
+static int mrf24j40_set_txpower(struct ieee802154_hw *hw, s32 mbm)
+{
+	struct mrf24j40 *devrec = hw->priv;
+	s32 small_scale;
+	u8 val;
+
+	if (0 >= mbm && mbm > -1000) {
+		val = TXPWRL_0 << TXPWRL_SHIFT;
+		small_scale = mbm;
+	} else if (-1000 >= mbm && mbm > -2000) {
+		val = TXPWRL_10 << TXPWRL_SHIFT;
+		small_scale = mbm + 1000;
+	} else if (-2000 >= mbm && mbm > -3000) {
+		val = TXPWRL_20 << TXPWRL_SHIFT;
+		small_scale = mbm + 2000;
+	} else if (-3000 >= mbm && mbm > -4000) {
+		val = TXPWRL_30 << TXPWRL_SHIFT;
+		small_scale = mbm + 3000;
+	} else {
+		return -EINVAL;
+	}
+
+	switch (small_scale) {
+	case 0:
+		val |= (TXPWRS_0 << TXPWRS_SHIFT);
+		break;
+	case -50:
+		val |= (TXPWRS_0_5 << TXPWRS_SHIFT);
+		break;
+	case -120:
+		val |= (TXPWRS_1_2 << TXPWRS_SHIFT);
+		break;
+	case -190:
+		val |= (TXPWRS_1_9 << TXPWRS_SHIFT);
+		break;
+	case -280:
+		val |= (TXPWRS_2_8 << TXPWRS_SHIFT);
+		break;
+	case -370:
+		val |= (TXPWRS_3_7 << TXPWRS_SHIFT);
+		break;
+	case -490:
+		val |= (TXPWRS_4_9 << TXPWRS_SHIFT);
+		break;
+	case -630:
+		val |= (TXPWRS_6_3 << TXPWRS_SHIFT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return regmap_update_bits(devrec->regmap_long, REG_RFCON3,
+				  TXPWRL_MASK | TXPWRS_MASK, val);
+}
+
+static int mrf24j40_set_promiscuous_mode(struct ieee802154_hw *hw, bool on)
+{
+	struct mrf24j40 *devrec = hw->priv;
+	int ret;
+
+	if (on) {
+		/* set PROMI, ERRPKT and NOACKRSP */
+		ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+					 BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+					 BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP);
+	} else {
+		/* clear PROMI, ERRPKT and NOACKRSP */
+		ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR,
+					 BIT_PROMI | BIT_ERRPKT | BIT_NOACKRSP,
+					 0);
+	}
 
 
 	return ret;
 	return ret;
 }
 }
 
 
 static const struct ieee802154_ops mrf24j40_ops = {
 static const struct ieee802154_ops mrf24j40_ops = {
 	.owner = THIS_MODULE,
 	.owner = THIS_MODULE,
-	.xmit_sync = mrf24j40_tx,
+	.xmit_async = mrf24j40_tx,
 	.ed = mrf24j40_ed,
 	.ed = mrf24j40_ed,
 	.start = mrf24j40_start,
 	.start = mrf24j40_start,
 	.stop = mrf24j40_stop,
 	.stop = mrf24j40_stop,
 	.set_channel = mrf24j40_set_channel,
 	.set_channel = mrf24j40_set_channel,
 	.set_hw_addr_filt = mrf24j40_filter,
 	.set_hw_addr_filt = mrf24j40_filter,
+	.set_csma_params = mrf24j40_csma_params,
+	.set_cca_mode = mrf24j40_set_cca_mode,
+	.set_cca_ed_level = mrf24j40_set_cca_ed_level,
+	.set_txpower = mrf24j40_set_txpower,
+	.set_promiscuous_mode = mrf24j40_set_promiscuous_mode,
 };
 };
 
 
-static irqreturn_t mrf24j40_isr(int irq, void *data)
+static void mrf24j40_intstat_complete(void *context)
 {
 {
-	struct mrf24j40 *devrec = data;
-	u8 intstat;
-	int ret;
+	struct mrf24j40 *devrec = context;
+	u8 intstat = devrec->irq_buf[1];
 
 
-	/* Read the interrupt status */
-	ret = read_short_reg(devrec, REG_INTSTAT, &intstat);
-	if (ret)
-		goto out;
+	enable_irq(devrec->spi->irq);
 
 
 	/* Check for TX complete */
 	/* Check for TX complete */
-	if (intstat & 0x1)
-		complete(&devrec->tx_complete);
+	if (intstat & BIT_TXNIF)
+		ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false);
 
 
 	/* Check for Rx */
 	/* Check for Rx */
-	if (intstat & 0x8)
+	if (intstat & BIT_RXIF)
 		mrf24j40_handle_rx(devrec);
 		mrf24j40_handle_rx(devrec);
+}
+
+static irqreturn_t mrf24j40_isr(int irq, void *data)
+{
+	struct mrf24j40 *devrec = data;
+	int ret;
+
+	disable_irq_nosync(irq);
+
+	devrec->irq_buf[0] = MRF24J40_READSHORT(REG_INTSTAT);
+	/* Read the interrupt status */
+	ret = spi_async(devrec->spi, &devrec->irq_msg);
+	if (ret) {
+		enable_irq(irq);
+		return IRQ_NONE;
+	}
 
 
-out:
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
 static int mrf24j40_hw_init(struct mrf24j40 *devrec)
 static int mrf24j40_hw_init(struct mrf24j40 *devrec)
 {
 {
+	u32 irq_type;
 	int ret;
 	int ret;
-	u8 val;
 
 
 	/* Initialize the device.
 	/* Initialize the device.
 		From datasheet section 3.2: Initialization. */
 		From datasheet section 3.2: Initialization. */
-	ret = write_short_reg(devrec, REG_SOFTRST, 0x07);
+	ret = regmap_write(devrec->regmap_short, REG_SOFTRST, 0x07);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_PACON2, 0x98);
+	ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_TXSTBL, 0x95);
+	ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_RFCON0, 0x03);
+	ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_RFCON1, 0x01);
+	ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_RFCON2, 0x80);
+	ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_RFCON6, 0x90);
+	ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_RFCON7, 0x80);
+	ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_RFCON8, 0x10);
+	ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_long_reg(devrec, REG_SLPCON1, 0x21);
+	ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_BBREG2, 0x80);
+	ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_CCAEDTH, 0x60);
+	ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_BBREG6, 0x40);
+	ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_RFCTL, 0x04);
+	ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
-	ret = write_short_reg(devrec, REG_RFCTL, 0x0);
+	ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
 	udelay(192);
 	udelay(192);
 
 
 	/* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */
 	/* Set RX Mode. RXMCR<1:0>: 0x0 normal, 0x1 promisc, 0x2 error */
-	ret = read_short_reg(devrec, REG_RXMCR, &val);
-	if (ret)
-		goto err_ret;
-
-	val &= ~0x3; /* Clear RX mode (normal) */
-
-	ret = write_short_reg(devrec, REG_RXMCR, val);
+	ret = regmap_update_bits(devrec->regmap_short, REG_RXMCR, 0x03, 0x00);
 	if (ret)
 	if (ret)
 		goto err_ret;
 		goto err_ret;
 
 
@@ -696,22 +1129,39 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec)
 		/* Enable external amplifier.
 		/* Enable external amplifier.
 		 * From MRF24J40MC datasheet section 1.3: Operation.
 		 * From MRF24J40MC datasheet section 1.3: Operation.
 		 */
 		 */
-		read_long_reg(devrec, REG_TESTMODE, &val);
-		val |= 0x7; /* Configure GPIO 0-2 to control amplifier */
-		write_long_reg(devrec, REG_TESTMODE, val);
+		regmap_update_bits(devrec->regmap_long, REG_TESTMODE, 0x07,
+				   0x07);
 
 
-		read_short_reg(devrec, REG_TRISGPIO, &val);
-		val |= 0x8; /* Set GPIO3 as output. */
-		write_short_reg(devrec, REG_TRISGPIO, val);
+		/* Set GPIO3 as output. */
+		regmap_update_bits(devrec->regmap_short, REG_TRISGPIO, 0x08,
+				   0x08);
 
 
-		read_short_reg(devrec, REG_GPIO, &val);
-		val |= 0x8; /* Set GPIO3 HIGH to enable U5 voltage regulator */
-		write_short_reg(devrec, REG_GPIO, val);
+		/* Set GPIO3 HIGH to enable U5 voltage regulator */
+		regmap_update_bits(devrec->regmap_short, REG_GPIO, 0x08, 0x08);
 
 
 		/* Reduce TX pwr to meet FCC requirements.
 		/* Reduce TX pwr to meet FCC requirements.
 		 * From MRF24J40MC datasheet section 3.1.1
 		 * From MRF24J40MC datasheet section 3.1.1
 		 */
 		 */
-		write_long_reg(devrec, REG_RFCON3, 0x28);
+		regmap_write(devrec->regmap_long, REG_RFCON3, 0x28);
+	}
+
+	irq_type = irq_get_trigger_type(devrec->spi->irq);
+	if (irq_type == IRQ_TYPE_EDGE_RISING ||
+	    irq_type == IRQ_TYPE_EDGE_FALLING)
+		dev_warn(&devrec->spi->dev,
+			 "Using edge triggered irq's are not recommended, because it can cause races and result in a non-functional driver!\n");
+	switch (irq_type) {
+	case IRQ_TYPE_EDGE_RISING:
+	case IRQ_TYPE_LEVEL_HIGH:
+		/* set interrupt polarity to rising */
+		ret = regmap_update_bits(devrec->regmap_long, REG_SLPCON0,
+					 BIT_INTEDGE, BIT_INTEDGE);
+		if (ret)
+			goto err_ret;
+		break;
+	default:
+		/* default is falling edge */
+		break;
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -720,67 +1170,178 @@ err_ret:
 	return ret;
 	return ret;
 }
 }
 
 
-static int mrf24j40_probe(struct spi_device *spi)
+static void
+mrf24j40_setup_tx_spi_messages(struct mrf24j40 *devrec)
 {
 {
-	int ret = -ENOMEM;
-	struct mrf24j40 *devrec;
+	spi_message_init(&devrec->tx_msg);
+	devrec->tx_msg.context = devrec;
+	devrec->tx_msg.complete = write_tx_buf_complete;
+	devrec->tx_hdr_trx.len = 2;
+	devrec->tx_hdr_trx.tx_buf = devrec->tx_hdr_buf;
+	spi_message_add_tail(&devrec->tx_hdr_trx, &devrec->tx_msg);
+	devrec->tx_len_trx.len = 2;
+	devrec->tx_len_trx.tx_buf = devrec->tx_len_buf;
+	spi_message_add_tail(&devrec->tx_len_trx, &devrec->tx_msg);
+	spi_message_add_tail(&devrec->tx_buf_trx, &devrec->tx_msg);
+
+	spi_message_init(&devrec->tx_post_msg);
+	devrec->tx_post_msg.context = devrec;
+	devrec->tx_post_trx.len = 2;
+	devrec->tx_post_trx.tx_buf = devrec->tx_post_buf;
+	spi_message_add_tail(&devrec->tx_post_trx, &devrec->tx_post_msg);
+}
 
 
-	dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
+static void
+mrf24j40_setup_rx_spi_messages(struct mrf24j40 *devrec)
+{
+	spi_message_init(&devrec->rx_msg);
+	devrec->rx_msg.context = devrec;
+	devrec->rx_trx.len = 2;
+	devrec->rx_trx.tx_buf = devrec->rx_buf;
+	devrec->rx_trx.rx_buf = devrec->rx_buf;
+	spi_message_add_tail(&devrec->rx_trx, &devrec->rx_msg);
+
+	spi_message_init(&devrec->rx_buf_msg);
+	devrec->rx_buf_msg.context = devrec;
+	devrec->rx_buf_msg.complete = mrf24j40_handle_rx_read_buf_complete;
+	devrec->rx_addr_trx.len = 2;
+	devrec->rx_addr_trx.tx_buf = devrec->rx_addr_buf;
+	spi_message_add_tail(&devrec->rx_addr_trx, &devrec->rx_buf_msg);
+	devrec->rx_fifo_buf_trx.rx_buf = devrec->rx_fifo_buf;
+	spi_message_add_tail(&devrec->rx_fifo_buf_trx, &devrec->rx_buf_msg);
+	devrec->rx_lqi_trx.len = 2;
+	devrec->rx_lqi_trx.rx_buf = devrec->rx_lqi_buf;
+	spi_message_add_tail(&devrec->rx_lqi_trx, &devrec->rx_buf_msg);
+}
 
 
-	devrec = devm_kzalloc(&spi->dev, sizeof(struct mrf24j40), GFP_KERNEL);
-	if (!devrec)
-		goto err_ret;
-	devrec->buf = devm_kzalloc(&spi->dev, 3, GFP_KERNEL);
-	if (!devrec->buf)
-		goto err_ret;
+static void
+mrf24j40_setup_irq_spi_messages(struct mrf24j40 *devrec)
+{
+	spi_message_init(&devrec->irq_msg);
+	devrec->irq_msg.context = devrec;
+	devrec->irq_msg.complete = mrf24j40_intstat_complete;
+	devrec->irq_trx.len = 2;
+	devrec->irq_trx.tx_buf = devrec->irq_buf;
+	devrec->irq_trx.rx_buf = devrec->irq_buf;
+	spi_message_add_tail(&devrec->irq_trx, &devrec->irq_msg);
+}
+
+static void  mrf24j40_phy_setup(struct mrf24j40 *devrec)
+{
+	ieee802154_random_extended_addr(&devrec->hw->phy->perm_extended_addr);
+	devrec->hw->phy->current_channel = 11;
+
+	/* mrf24j40 supports max_minbe 0 - 3 */
+	devrec->hw->phy->supported.max_minbe = 3;
+	/* datasheet doesn't say anything about max_be, but we have min_be
+	 * So we assume the max_be default.
+	 */
+	devrec->hw->phy->supported.min_maxbe = 5;
+	devrec->hw->phy->supported.max_maxbe = 5;
+
+	devrec->hw->phy->cca.mode = NL802154_CCA_CARRIER;
+	devrec->hw->phy->supported.cca_modes = BIT(NL802154_CCA_ENERGY) |
+					       BIT(NL802154_CCA_CARRIER) |
+					       BIT(NL802154_CCA_ENERGY_CARRIER);
+	devrec->hw->phy->supported.cca_opts = BIT(NL802154_CCA_OPT_ENERGY_CARRIER_AND);
+
+	devrec->hw->phy->cca_ed_level = -6900;
+	devrec->hw->phy->supported.cca_ed_levels = mrf24j40_ed_levels;
+	devrec->hw->phy->supported.cca_ed_levels_size = ARRAY_SIZE(mrf24j40_ed_levels);
+
+	switch (spi_get_device_id(devrec->spi)->driver_data) {
+	case MRF24J40:
+	case MRF24J40MA:
+		devrec->hw->phy->supported.tx_powers = mrf24j40ma_powers;
+		devrec->hw->phy->supported.tx_powers_size = ARRAY_SIZE(mrf24j40ma_powers);
+		devrec->hw->phy->flags |= WPAN_PHY_FLAG_TXPOWER;
+		break;
+	default:
+		break;
+	}
+}
 
 
-	spi->mode = SPI_MODE_0; /* TODO: Is this appropriate for right here? */
-	if (spi->max_speed_hz > MAX_SPI_SPEED_HZ)
-		spi->max_speed_hz = MAX_SPI_SPEED_HZ;
+static int mrf24j40_probe(struct spi_device *spi)
+{
+	int ret = -ENOMEM, irq_type;
+	struct ieee802154_hw *hw;
+	struct mrf24j40 *devrec;
 
 
-	mutex_init(&devrec->buffer_mutex);
-	init_completion(&devrec->tx_complete);
-	devrec->spi = spi;
-	spi_set_drvdata(spi, devrec);
+	dev_info(&spi->dev, "probe(). IRQ: %d\n", spi->irq);
 
 
 	/* Register with the 802154 subsystem */
 	/* Register with the 802154 subsystem */
 
 
-	devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
-	if (!devrec->hw)
+	hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops);
+	if (!hw)
 		goto err_ret;
 		goto err_ret;
 
 
-	devrec->hw->priv = devrec;
-	devrec->hw->parent = &devrec->spi->dev;
+	devrec = hw->priv;
+	devrec->spi = spi;
+	spi_set_drvdata(spi, devrec);
+	devrec->hw = hw;
+	devrec->hw->parent = &spi->dev;
 	devrec->hw->phy->supported.channels[0] = CHANNEL_MASK;
 	devrec->hw->phy->supported.channels[0] = CHANNEL_MASK;
-	devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
+	devrec->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
+			    IEEE802154_HW_CSMA_PARAMS |
+			    IEEE802154_HW_PROMISCUOUS;
+
+	devrec->hw->phy->flags = WPAN_PHY_FLAG_CCA_MODE |
+				 WPAN_PHY_FLAG_CCA_ED_LEVEL;
+
+	mrf24j40_setup_tx_spi_messages(devrec);
+	mrf24j40_setup_rx_spi_messages(devrec);
+	mrf24j40_setup_irq_spi_messages(devrec);
+
+	devrec->regmap_short = devm_regmap_init_spi(spi,
+						    &mrf24j40_short_regmap);
+	if (IS_ERR(devrec->regmap_short)) {
+		ret = PTR_ERR(devrec->regmap_short);
+		dev_err(&spi->dev, "Failed to allocate short register map: %d\n",
+			ret);
+		goto err_register_device;
+	}
 
 
-	dev_dbg(printdev(devrec), "registered mrf24j40\n");
-	ret = ieee802154_register_hw(devrec->hw);
-	if (ret)
+	devrec->regmap_long = devm_regmap_init(&spi->dev,
+					       &mrf24j40_long_regmap_bus,
+					       spi, &mrf24j40_long_regmap);
+	if (IS_ERR(devrec->regmap_long)) {
+		ret = PTR_ERR(devrec->regmap_long);
+		dev_err(&spi->dev, "Failed to allocate long register map: %d\n",
+			ret);
 		goto err_register_device;
 		goto err_register_device;
+	}
+
+	if (spi->max_speed_hz > MAX_SPI_SPEED_HZ) {
+		dev_warn(&spi->dev, "spi clock above possible maximum: %d",
+			 MAX_SPI_SPEED_HZ);
+		return -EINVAL;
+	}
 
 
 	ret = mrf24j40_hw_init(devrec);
 	ret = mrf24j40_hw_init(devrec);
 	if (ret)
 	if (ret)
-		goto err_hw_init;
+		goto err_register_device;
 
 
-	ret = devm_request_threaded_irq(&spi->dev,
-					spi->irq,
-					NULL,
-					mrf24j40_isr,
-					IRQF_TRIGGER_LOW|IRQF_ONESHOT,
-					dev_name(&spi->dev),
-					devrec);
+	mrf24j40_phy_setup(devrec);
 
 
+	/* request IRQF_TRIGGER_LOW as fallback default */
+	irq_type = irq_get_trigger_type(spi->irq);
+	if (!irq_type)
+		irq_type = IRQF_TRIGGER_LOW;
+
+	ret = devm_request_irq(&spi->dev, spi->irq, mrf24j40_isr,
+			       irq_type, dev_name(&spi->dev), devrec);
 	if (ret) {
 	if (ret) {
 		dev_err(printdev(devrec), "Unable to get IRQ");
 		dev_err(printdev(devrec), "Unable to get IRQ");
-		goto err_irq;
+		goto err_register_device;
 	}
 	}
 
 
+	dev_dbg(printdev(devrec), "registered mrf24j40\n");
+	ret = ieee802154_register_hw(devrec->hw);
+	if (ret)
+		goto err_register_device;
+
 	return 0;
 	return 0;
 
 
-err_irq:
-err_hw_init:
-	ieee802154_unregister_hw(devrec->hw);
 err_register_device:
 err_register_device:
 	ieee802154_free_hw(devrec->hw);
 	ieee802154_free_hw(devrec->hw);
 err_ret:
 err_ret:
@@ -801,6 +1362,14 @@ static int mrf24j40_remove(struct spi_device *spi)
 	return 0;
 	return 0;
 }
 }
 
 
+static const struct of_device_id mrf24j40_of_match[] = {
+	{ .compatible = "microchip,mrf24j40", .data = (void *)MRF24J40 },
+	{ .compatible = "microchip,mrf24j40ma", .data = (void *)MRF24J40MA },
+	{ .compatible = "microchip,mrf24j40mc", .data = (void *)MRF24J40MC },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, mrf24j40_of_match);
+
 static const struct spi_device_id mrf24j40_ids[] = {
 static const struct spi_device_id mrf24j40_ids[] = {
 	{ "mrf24j40", MRF24J40 },
 	{ "mrf24j40", MRF24J40 },
 	{ "mrf24j40ma", MRF24J40MA },
 	{ "mrf24j40ma", MRF24J40MA },
@@ -811,6 +1380,7 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
 
 
 static struct spi_driver mrf24j40_driver = {
 static struct spi_driver mrf24j40_driver = {
 	.driver = {
 	.driver = {
+		.of_match_table = of_match_ptr(mrf24j40_of_match),
 		.name = "mrf24j40",
 		.name = "mrf24j40",
 		.owner = THIS_MODULE,
 		.owner = THIS_MODULE,
 	},
 	},

+ 21 - 1
include/linux/ieee802154.h

@@ -25,12 +25,22 @@
 
 
 #include <linux/types.h>
 #include <linux/types.h>
 #include <linux/random.h>
 #include <linux/random.h>
-#include <asm/byteorder.h>
 
 
 #define IEEE802154_MTU			127
 #define IEEE802154_MTU			127
 #define IEEE802154_ACK_PSDU_LEN		5
 #define IEEE802154_ACK_PSDU_LEN		5
 #define IEEE802154_MIN_PSDU_LEN		9
 #define IEEE802154_MIN_PSDU_LEN		9
 #define IEEE802154_FCS_LEN		2
 #define IEEE802154_FCS_LEN		2
+#define IEEE802154_MAX_AUTH_TAG_LEN	16
+
+/*  General MAC frame format:
+ *  2 bytes: Frame Control
+ *  1 byte:  Sequence Number
+ * 20 bytes: Addressing fields
+ * 14 bytes: Auxiliary Security Header
+ */
+#define IEEE802154_MAX_HEADER_LEN	(2 + 1 + 20 + 14)
+#define IEEE802154_MIN_HEADER_LEN	(IEEE802154_ACK_PSDU_LEN - \
+					 IEEE802154_FCS_LEN)
 
 
 #define IEEE802154_PAN_ID_BROADCAST	0xffff
 #define IEEE802154_PAN_ID_BROADCAST	0xffff
 #define IEEE802154_ADDR_SHORT_BROADCAST	0xffff
 #define IEEE802154_ADDR_SHORT_BROADCAST	0xffff
@@ -207,6 +217,7 @@ enum {
 
 
 /* frame control handling */
 /* frame control handling */
 #define IEEE802154_FCTL_FTYPE		0x0003
 #define IEEE802154_FCTL_FTYPE		0x0003
+#define IEEE802154_FCTL_ACKREQ		0x0020
 #define IEEE802154_FCTL_INTRA_PAN	0x0040
 #define IEEE802154_FCTL_INTRA_PAN	0x0040
 
 
 #define IEEE802154_FTYPE_DATA		0x0001
 #define IEEE802154_FTYPE_DATA		0x0001
@@ -221,6 +232,15 @@ static inline int ieee802154_is_data(__le16 fc)
 		cpu_to_le16(IEEE802154_FTYPE_DATA);
 		cpu_to_le16(IEEE802154_FTYPE_DATA);
 }
 }
 
 
+/**
+ * ieee802154_is_ackreq - check if acknowledgment request bit is set
+ * @fc: frame control bytes in little-endian byteorder
+ */
+static inline bool ieee802154_is_ackreq(__le16 fc)
+{
+	return fc & cpu_to_le16(IEEE802154_FCTL_ACKREQ);
+}
+
 /**
 /**
  * ieee802154_is_intra_pan - check if intra pan id communication
  * ieee802154_is_intra_pan - check if intra pan id communication
  * @fc: frame control bytes in little-endian byteorder
  * @fc: frame control bytes in little-endian byteorder

+ 10 - 0
include/net/6lowpan.h

@@ -61,6 +61,16 @@
 #define UIP_PROTO_UDP			17 /* ipv6 next header value for UDP */
 #define UIP_PROTO_UDP			17 /* ipv6 next header value for UDP */
 #define UIP_FRAGH_LEN			8  /* ipv6 fragment header size */
 #define UIP_FRAGH_LEN			8  /* ipv6 fragment header size */
 
 
+#define EUI64_ADDR_LEN		8
+
+#define LOWPAN_NHC_MAX_ID_LEN	1
+/* Max IPHC Header len without IPv6 hdr specific inline data.
+ * Useful for getting the "extra" bytes we need at worst case compression.
+ *
+ * LOWPAN_IPHC + CID + LOWPAN_NHC_MAX_ID_LEN
+ */
+#define LOWPAN_IPHC_MAX_HEADER_LEN	(2 + 1 + LOWPAN_NHC_MAX_ID_LEN)
+
 /*
 /*
  * ipv6 address based on mac
  * ipv6 address based on mac
  * second bit-flip (Universe/Local) is done according RFC2464
  * second bit-flip (Universe/Local) is done according RFC2464

+ 5 - 0
include/net/bluetooth/bluetooth.h

@@ -122,11 +122,14 @@ struct bt_voice {
 __printf(1, 2)
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
 __printf(1, 2)
+void bt_warn(const char *fmt, ...);
+__printf(1, 2)
 void bt_err(const char *fmt, ...);
 void bt_err(const char *fmt, ...);
 __printf(1, 2)
 __printf(1, 2)
 void bt_err_ratelimited(const char *fmt, ...);
 void bt_err_ratelimited(const char *fmt, ...);
 
 
 #define BT_INFO(fmt, ...)	bt_info(fmt "\n", ##__VA_ARGS__)
 #define BT_INFO(fmt, ...)	bt_info(fmt "\n", ##__VA_ARGS__)
+#define BT_WARN(fmt, ...)	bt_warn(fmt "\n", ##__VA_ARGS__)
 #define BT_ERR(fmt, ...)	bt_err(fmt "\n", ##__VA_ARGS__)
 #define BT_ERR(fmt, ...)	bt_err(fmt "\n", ##__VA_ARGS__)
 #define BT_DBG(fmt, ...)	pr_debug(fmt "\n", ##__VA_ARGS__)
 #define BT_DBG(fmt, ...)	pr_debug(fmt "\n", ##__VA_ARGS__)
 
 
@@ -134,6 +137,8 @@ void bt_err_ratelimited(const char *fmt, ...);
 
 
 #define bt_dev_info(hdev, fmt, ...)				\
 #define bt_dev_info(hdev, fmt, ...)				\
 	BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
 	BT_INFO("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
+#define bt_dev_warn(hdev, fmt, ...)				\
+	BT_WARN("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
 #define bt_dev_err(hdev, fmt, ...)				\
 #define bt_dev_err(hdev, fmt, ...)				\
 	BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
 	BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
 #define bt_dev_dbg(hdev, fmt, ...)				\
 #define bt_dev_dbg(hdev, fmt, ...)				\

+ 4 - 0
include/net/bluetooth/hci.h

@@ -44,6 +44,8 @@
 #define HCI_DEV_DOWN			4
 #define HCI_DEV_DOWN			4
 #define HCI_DEV_SUSPEND			5
 #define HCI_DEV_SUSPEND			5
 #define HCI_DEV_RESUME			6
 #define HCI_DEV_RESUME			6
+#define HCI_DEV_OPEN			7
+#define HCI_DEV_CLOSE			8
 
 
 /* HCI notify events */
 /* HCI notify events */
 #define HCI_NOTIFY_CONN_ADD		1
 #define HCI_NOTIFY_CONN_ADD		1
@@ -238,6 +240,7 @@ enum {
 	HCI_LE_SCAN_INTERRUPTED,
 	HCI_LE_SCAN_INTERRUPTED,
 
 
 	HCI_DUT_MODE,
 	HCI_DUT_MODE,
+	HCI_VENDOR_DIAG,
 	HCI_FORCE_BREDR_SMP,
 	HCI_FORCE_BREDR_SMP,
 	HCI_FORCE_STATIC_ADDR,
 	HCI_FORCE_STATIC_ADDR,
 
 
@@ -260,6 +263,7 @@ enum {
 #define HCI_ACLDATA_PKT		0x02
 #define HCI_ACLDATA_PKT		0x02
 #define HCI_SCODATA_PKT		0x03
 #define HCI_SCODATA_PKT		0x03
 #define HCI_EVENT_PKT		0x04
 #define HCI_EVENT_PKT		0x04
+#define HCI_DIAG_PKT		0xf0
 #define HCI_VENDOR_PKT		0xff
 #define HCI_VENDOR_PKT		0xff
 
 
 /* HCI packet types */
 /* HCI packet types */

+ 5 - 0
include/net/bluetooth/hci_core.h

@@ -398,6 +398,7 @@ struct hci_dev {
 	int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
 	int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
 	void (*notify)(struct hci_dev *hdev, unsigned int evt);
 	void (*notify)(struct hci_dev *hdev, unsigned int evt);
 	void (*hw_error)(struct hci_dev *hdev, u8 code);
 	void (*hw_error)(struct hci_dev *hdev, u8 code);
+	int (*set_diag)(struct hci_dev *hdev, bool enable);
 	int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 	int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr);
 };
 };
 
 
@@ -1066,6 +1067,7 @@ int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
 
 
 int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
 int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
+int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb);
 
 
 void hci_init_sysfs(struct hci_dev *hdev);
 void hci_init_sysfs(struct hci_dev *hdev);
 void hci_conn_init_sysfs(struct hci_conn *conn);
 void hci_conn_init_sysfs(struct hci_conn *conn);
@@ -1349,6 +1351,9 @@ void hci_send_sco(struct hci_conn *conn, struct sk_buff *skb);
 
 
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode);
 
 
+struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+			     const void *param, u32 timeout);
+
 /* ----- HCI Sockets ----- */
 /* ----- HCI Sockets ----- */
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
 void hci_send_to_sock(struct hci_dev *hdev, struct sk_buff *skb);
 void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,
 void hci_send_to_channel(unsigned short channel, struct sk_buff *skb,

+ 10 - 0
include/net/bluetooth/hci_mon.h

@@ -39,6 +39,10 @@ struct hci_mon_hdr {
 #define HCI_MON_ACL_RX_PKT	5
 #define HCI_MON_ACL_RX_PKT	5
 #define HCI_MON_SCO_TX_PKT	6
 #define HCI_MON_SCO_TX_PKT	6
 #define HCI_MON_SCO_RX_PKT	7
 #define HCI_MON_SCO_RX_PKT	7
+#define HCI_MON_OPEN_INDEX	8
+#define HCI_MON_CLOSE_INDEX	9
+#define HCI_MON_INDEX_INFO	10
+#define HCI_MON_VENDOR_DIAG	11
 
 
 struct hci_mon_new_index {
 struct hci_mon_new_index {
 	__u8		type;
 	__u8		type;
@@ -48,4 +52,10 @@ struct hci_mon_new_index {
 } __packed;
 } __packed;
 #define HCI_MON_NEW_INDEX_SIZE 16
 #define HCI_MON_NEW_INDEX_SIZE 16
 
 
+struct hci_mon_index_info {
+	bdaddr_t	bdaddr;
+	__le16		manufacturer;
+} __packed;
+#define HCI_MON_INDEX_INFO_SIZE 8
+
 #endif /* __HCI_MON_H */
 #endif /* __HCI_MON_H */

+ 164 - 0
include/net/cfg802154.h

@@ -27,6 +27,16 @@
 struct wpan_phy;
 struct wpan_phy;
 struct wpan_phy_cca;
 struct wpan_phy_cca;
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+struct ieee802154_llsec_device_key;
+struct ieee802154_llsec_seclevel;
+struct ieee802154_llsec_params;
+struct ieee802154_llsec_device;
+struct ieee802154_llsec_table;
+struct ieee802154_llsec_key_id;
+struct ieee802154_llsec_key;
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 struct cfg802154_ops {
 struct cfg802154_ops {
 	struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
 	struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
 							   const char *name,
 							   const char *name,
@@ -65,6 +75,51 @@ struct cfg802154_ops {
 				struct wpan_dev *wpan_dev, bool mode);
 				struct wpan_dev *wpan_dev, bool mode);
 	int	(*set_ackreq_default)(struct wpan_phy *wpan_phy,
 	int	(*set_ackreq_default)(struct wpan_phy *wpan_phy,
 				      struct wpan_dev *wpan_dev, bool ackreq);
 				      struct wpan_dev *wpan_dev, bool ackreq);
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	void	(*get_llsec_table)(struct wpan_phy *wpan_phy,
+				   struct wpan_dev *wpan_dev,
+				   struct ieee802154_llsec_table **table);
+	void	(*lock_llsec_table)(struct wpan_phy *wpan_phy,
+				    struct wpan_dev *wpan_dev);
+	void	(*unlock_llsec_table)(struct wpan_phy *wpan_phy,
+				      struct wpan_dev *wpan_dev);
+	/* TODO remove locking/get table callbacks, this is part of the
+	 * nl802154 interface and should be accessible from ieee802154 layer.
+	 */
+	int	(*get_llsec_params)(struct wpan_phy *wpan_phy,
+				    struct wpan_dev *wpan_dev,
+				    struct ieee802154_llsec_params *params);
+	int	(*set_llsec_params)(struct wpan_phy *wpan_phy,
+				    struct wpan_dev *wpan_dev,
+				    const struct ieee802154_llsec_params *params,
+				    int changed);
+	int	(*add_llsec_key)(struct wpan_phy *wpan_phy,
+				 struct wpan_dev *wpan_dev,
+				 const struct ieee802154_llsec_key_id *id,
+				 const struct ieee802154_llsec_key *key);
+	int	(*del_llsec_key)(struct wpan_phy *wpan_phy,
+				 struct wpan_dev *wpan_dev,
+				 const struct ieee802154_llsec_key_id *id);
+	int	(*add_seclevel)(struct wpan_phy *wpan_phy,
+				 struct wpan_dev *wpan_dev,
+				 const struct ieee802154_llsec_seclevel *sl);
+	int	(*del_seclevel)(struct wpan_phy *wpan_phy,
+				 struct wpan_dev *wpan_dev,
+				 const struct ieee802154_llsec_seclevel *sl);
+	int	(*add_device)(struct wpan_phy *wpan_phy,
+			      struct wpan_dev *wpan_dev,
+			      const struct ieee802154_llsec_device *dev);
+	int	(*del_device)(struct wpan_phy *wpan_phy,
+			      struct wpan_dev *wpan_dev, __le64 extended_addr);
+	int	(*add_devkey)(struct wpan_phy *wpan_phy,
+			      struct wpan_dev *wpan_dev,
+			      __le64 extended_addr,
+			      const struct ieee802154_llsec_device_key *key);
+	int	(*del_devkey)(struct wpan_phy *wpan_phy,
+			      struct wpan_dev *wpan_dev,
+			      __le64 extended_addr,
+			      const struct ieee802154_llsec_device_key *key);
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 };
 
 
 static inline bool
 static inline bool
@@ -167,6 +222,102 @@ struct wpan_phy {
 	char priv[0] __aligned(NETDEV_ALIGN);
 	char priv[0] __aligned(NETDEV_ALIGN);
 };
 };
 
 
+struct ieee802154_addr {
+	u8 mode;
+	__le16 pan_id;
+	union {
+		__le16 short_addr;
+		__le64 extended_addr;
+	};
+};
+
+struct ieee802154_llsec_key_id {
+	u8 mode;
+	u8 id;
+	union {
+		struct ieee802154_addr device_addr;
+		__le32 short_source;
+		__le64 extended_source;
+	};
+};
+
+#define IEEE802154_LLSEC_KEY_SIZE 16
+
+struct ieee802154_llsec_key {
+	u8 frame_types;
+	u32 cmd_frame_ids;
+	/* TODO replace with NL802154_KEY_SIZE */
+	u8 key[IEEE802154_LLSEC_KEY_SIZE];
+};
+
+struct ieee802154_llsec_key_entry {
+	struct list_head list;
+
+	struct ieee802154_llsec_key_id id;
+	struct ieee802154_llsec_key *key;
+};
+
+struct ieee802154_llsec_params {
+	bool enabled;
+
+	__be32 frame_counter;
+	u8 out_level;
+	struct ieee802154_llsec_key_id out_key;
+
+	__le64 default_key_source;
+
+	__le16 pan_id;
+	__le64 hwaddr;
+	__le64 coord_hwaddr;
+	__le16 coord_shortaddr;
+};
+
+struct ieee802154_llsec_table {
+	struct list_head keys;
+	struct list_head devices;
+	struct list_head security_levels;
+};
+
+struct ieee802154_llsec_seclevel {
+	struct list_head list;
+
+	u8 frame_type;
+	u8 cmd_frame_id;
+	bool device_override;
+	u32 sec_levels;
+};
+
+struct ieee802154_llsec_device {
+	struct list_head list;
+
+	__le16 pan_id;
+	__le16 short_addr;
+	__le64 hwaddr;
+	u32 frame_counter;
+	bool seclevel_exempt;
+
+	u8 key_mode;
+	struct list_head keys;
+};
+
+struct ieee802154_llsec_device_key {
+	struct list_head list;
+
+	struct ieee802154_llsec_key_id key_id;
+	u32 frame_counter;
+};
+
+struct wpan_dev_header_ops {
+	/* TODO create callback currently assumes ieee802154_mac_cb inside
+	 * skb->cb. This should be changed to give these information as
+	 * parameter.
+	 */
+	int	(*create)(struct sk_buff *skb, struct net_device *dev,
+			  const struct ieee802154_addr *daddr,
+			  const struct ieee802154_addr *saddr,
+			  unsigned int len);
+};
+
 struct wpan_dev {
 struct wpan_dev {
 	struct wpan_phy *wpan_phy;
 	struct wpan_phy *wpan_phy;
 	int iftype;
 	int iftype;
@@ -175,6 +326,8 @@ struct wpan_dev {
 	struct list_head list;
 	struct list_head list;
 	struct net_device *netdev;
 	struct net_device *netdev;
 
 
+	const struct wpan_dev_header_ops *header_ops;
+
 	/* lowpan interface, set when the wpan_dev belongs to one lowpan_dev */
 	/* lowpan interface, set when the wpan_dev belongs to one lowpan_dev */
 	struct net_device *lowpan_dev;
 	struct net_device *lowpan_dev;
 
 
@@ -205,6 +358,17 @@ struct wpan_dev {
 
 
 #define to_phy(_dev)	container_of(_dev, struct wpan_phy, dev)
 #define to_phy(_dev)	container_of(_dev, struct wpan_phy, dev)
 
 
+static inline int
+wpan_dev_hard_header(struct sk_buff *skb, struct net_device *dev,
+		     const struct ieee802154_addr *daddr,
+		     const struct ieee802154_addr *saddr,
+		     unsigned int len)
+{
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+
+	return wpan_dev->header_ops->create(skb, dev, daddr, saddr, len);
+}
+
 struct wpan_phy *
 struct wpan_phy *
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size);
 static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)
 static inline void wpan_phy_set_dev(struct wpan_phy *phy, struct device *dev)

+ 1 - 85
include/net/ieee802154_netdev.h

@@ -50,15 +50,6 @@ struct ieee802154_sechdr {
 	};
 	};
 };
 };
 
 
-struct ieee802154_addr {
-	u8 mode;
-	__le16 pan_id;
-	union {
-		__le16 short_addr;
-		__le64 extended_addr;
-	};
-};
-
 struct ieee802154_hdr_fc {
 struct ieee802154_hdr_fc {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
 #if defined(__LITTLE_ENDIAN_BITFIELD)
 	u16 type:3,
 	u16 type:3,
@@ -99,7 +90,7 @@ struct ieee802154_hdr {
  * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame
  * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame
  * version, if SECEN is set.
  * version, if SECEN is set.
  */
  */
-int ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr);
+int ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr);
 
 
 /* pulls the entire 802.15.4 header off of the skb, including the security
 /* pulls the entire 802.15.4 header off of the skb, including the security
  * header, and performs pan id decompression
  * header, and performs pan id decompression
@@ -243,38 +234,6 @@ static inline struct ieee802154_mac_cb *mac_cb_init(struct sk_buff *skb)
 	return mac_cb(skb);
 	return mac_cb(skb);
 }
 }
 
 
-#define IEEE802154_LLSEC_KEY_SIZE 16
-
-struct ieee802154_llsec_key_id {
-	u8 mode;
-	u8 id;
-	union {
-		struct ieee802154_addr device_addr;
-		__le32 short_source;
-		__le64 extended_source;
-	};
-};
-
-struct ieee802154_llsec_key {
-	u8 frame_types;
-	u32 cmd_frame_ids;
-	u8 key[IEEE802154_LLSEC_KEY_SIZE];
-};
-
-struct ieee802154_llsec_key_entry {
-	struct list_head list;
-
-	struct ieee802154_llsec_key_id id;
-	struct ieee802154_llsec_key *key;
-};
-
-struct ieee802154_llsec_device_key {
-	struct list_head list;
-
-	struct ieee802154_llsec_key_id key_id;
-	u32 frame_counter;
-};
-
 enum {
 enum {
 	IEEE802154_LLSEC_DEVKEY_IGNORE,
 	IEEE802154_LLSEC_DEVKEY_IGNORE,
 	IEEE802154_LLSEC_DEVKEY_RESTRICT,
 	IEEE802154_LLSEC_DEVKEY_RESTRICT,
@@ -283,49 +242,6 @@ enum {
 	__IEEE802154_LLSEC_DEVKEY_MAX,
 	__IEEE802154_LLSEC_DEVKEY_MAX,
 };
 };
 
 
-struct ieee802154_llsec_device {
-	struct list_head list;
-
-	__le16 pan_id;
-	__le16 short_addr;
-	__le64 hwaddr;
-	u32 frame_counter;
-	bool seclevel_exempt;
-
-	u8 key_mode;
-	struct list_head keys;
-};
-
-struct ieee802154_llsec_seclevel {
-	struct list_head list;
-
-	u8 frame_type;
-	u8 cmd_frame_id;
-	bool device_override;
-	u32 sec_levels;
-};
-
-struct ieee802154_llsec_params {
-	bool enabled;
-
-	__be32 frame_counter;
-	u8 out_level;
-	struct ieee802154_llsec_key_id out_key;
-
-	__le64 default_key_source;
-
-	__le16 pan_id;
-	__le64 hwaddr;
-	__le64 coord_hwaddr;
-	__le16 coord_shortaddr;
-};
-
-struct ieee802154_llsec_table {
-	struct list_head keys;
-	struct list_head devices;
-	struct list_head security_levels;
-};
-
 #define IEEE802154_MAC_SCAN_ED		0
 #define IEEE802154_MAC_SCAN_ED		0
 #define IEEE802154_MAC_SCAN_ACTIVE	1
 #define IEEE802154_MAC_SCAN_ACTIVE	1
 #define IEEE802154_MAC_SCAN_PASSIVE	2
 #define IEEE802154_MAC_SCAN_PASSIVE	2

+ 1 - 9
include/net/mac802154.h

@@ -23,14 +23,6 @@
 
 
 #include <net/cfg802154.h>
 #include <net/cfg802154.h>
 
 
-/* General MAC frame format:
- *  2 bytes: Frame Control
- *  1 byte:  Sequence Number
- * 20 bytes: Addressing fields
- * 14 bytes: Auxiliary Security Header
- */
-#define MAC802154_FRAME_HARD_HEADER_LEN		(2 + 1 + 20 + 14)
-
 /**
 /**
  * enum ieee802154_hw_addr_filt_flags - hardware address filtering flags
  * enum ieee802154_hw_addr_filt_flags - hardware address filtering flags
  *
  *
@@ -256,7 +248,7 @@ struct ieee802154_ops {
 static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb)
 static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb)
 {
 {
 	/* return some invalid fc on failure */
 	/* return some invalid fc on failure */
-	if (unlikely(skb->mac_len < 2)) {
+	if (unlikely(skb->len < 2)) {
 		WARN_ON(1);
 		WARN_ON(1);
 		return cpu_to_le16(0);
 		return cpu_to_le16(0);
 	}
 	}

+ 18 - 0
include/net/netlink.h

@@ -1003,6 +1003,15 @@ static inline __be32 nla_get_be32(const struct nlattr *nla)
 	return *(__be32 *) nla_data(nla);
 	return *(__be32 *) nla_data(nla);
 }
 }
 
 
+/**
+ * nla_get_le32 - return payload of __le32 attribute
+ * @nla: __le32 netlink attribute
+ */
+static inline __le32 nla_get_le32(const struct nlattr *nla)
+{
+	return *(__le32 *) nla_data(nla);
+}
+
 /**
 /**
  * nla_get_u16 - return payload of u16 attribute
  * nla_get_u16 - return payload of u16 attribute
  * @nla: u16 netlink attribute
  * @nla: u16 netlink attribute
@@ -1065,6 +1074,15 @@ static inline __be64 nla_get_be64(const struct nlattr *nla)
 	return tmp;
 	return tmp;
 }
 }
 
 
+/**
+ * nla_get_le64 - return payload of __le64 attribute
+ * @nla: __le64 netlink attribute
+ */
+static inline __le64 nla_get_le64(const struct nlattr *nla)
+{
+	return *(__le64 *) nla_data(nla);
+}
+
 /**
 /**
  * nla_get_s32 - return payload of s32 attribute
  * nla_get_s32 - return payload of s32 attribute
  * @nla: s32 netlink attribute
  * @nla: s32 netlink attribute

+ 191 - 0
include/net/nl802154.h

@@ -56,6 +56,22 @@ enum nl802154_commands {
 
 
 	/* add new commands above here */
 	/* add new commands above here */
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	NL802154_CMD_SET_SEC_PARAMS,
+	NL802154_CMD_GET_SEC_KEY,		/* can dump */
+	NL802154_CMD_NEW_SEC_KEY,
+	NL802154_CMD_DEL_SEC_KEY,
+	NL802154_CMD_GET_SEC_DEV,		/* can dump */
+	NL802154_CMD_NEW_SEC_DEV,
+	NL802154_CMD_DEL_SEC_DEV,
+	NL802154_CMD_GET_SEC_DEVKEY,		/* can dump */
+	NL802154_CMD_NEW_SEC_DEVKEY,
+	NL802154_CMD_DEL_SEC_DEVKEY,
+	NL802154_CMD_GET_SEC_LEVEL,		/* can dump */
+	NL802154_CMD_NEW_SEC_LEVEL,
+	NL802154_CMD_DEL_SEC_LEVEL,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 	/* used to define NL802154_CMD_MAX below */
 	/* used to define NL802154_CMD_MAX below */
 	__NL802154_CMD_AFTER_LAST,
 	__NL802154_CMD_AFTER_LAST,
 	NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
 	NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
@@ -110,6 +126,18 @@ enum nl802154_attrs {
 
 
 	/* add attributes here, update the policy in nl802154.c */
 	/* add attributes here, update the policy in nl802154.c */
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	NL802154_ATTR_SEC_ENABLED,
+	NL802154_ATTR_SEC_OUT_LEVEL,
+	NL802154_ATTR_SEC_OUT_KEY_ID,
+	NL802154_ATTR_SEC_FRAME_COUNTER,
+
+	NL802154_ATTR_SEC_LEVEL,
+	NL802154_ATTR_SEC_DEVICE,
+	NL802154_ATTR_SEC_DEVKEY,
+	NL802154_ATTR_SEC_KEY,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 	__NL802154_ATTR_AFTER_LAST,
 	__NL802154_ATTR_AFTER_LAST,
 	NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
 	NL802154_ATTR_MAX = __NL802154_ATTR_AFTER_LAST - 1
 };
 };
@@ -247,4 +275,167 @@ enum nl802154_supported_bool_states {
 	NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1
 	NL802154_SUPPORTED_BOOL_MAX = __NL802154_SUPPORTED_BOOL_AFTER_LAST - 1
 };
 };
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+
+enum nl802154_dev_addr_modes {
+	NL802154_DEV_ADDR_NONE,
+	__NL802154_DEV_ADDR_INVALID,
+	NL802154_DEV_ADDR_SHORT,
+	NL802154_DEV_ADDR_EXTENDED,
+
+	/* keep last */
+	__NL802154_DEV_ADDR_AFTER_LAST,
+	NL802154_DEV_ADDR_MAX = __NL802154_DEV_ADDR_AFTER_LAST - 1
+};
+
+enum nl802154_dev_addr_attrs {
+	NL802154_DEV_ADDR_ATTR_UNSPEC,
+
+	NL802154_DEV_ADDR_ATTR_PAN_ID,
+	NL802154_DEV_ADDR_ATTR_MODE,
+	NL802154_DEV_ADDR_ATTR_SHORT,
+	NL802154_DEV_ADDR_ATTR_EXTENDED,
+
+	/* keep last */
+	__NL802154_DEV_ADDR_ATTR_AFTER_LAST,
+	NL802154_DEV_ADDR_ATTR_MAX = __NL802154_DEV_ADDR_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_key_id_modes {
+	NL802154_KEY_ID_MODE_IMPLICIT,
+	NL802154_KEY_ID_MODE_INDEX,
+	NL802154_KEY_ID_MODE_INDEX_SHORT,
+	NL802154_KEY_ID_MODE_INDEX_EXTENDED,
+
+	/* keep last */
+	__NL802154_KEY_ID_MODE_AFTER_LAST,
+	NL802154_KEY_ID_MODE_MAX = __NL802154_KEY_ID_MODE_AFTER_LAST - 1
+};
+
+enum nl802154_key_id_attrs {
+	NL802154_KEY_ID_ATTR_UNSPEC,
+
+	NL802154_KEY_ID_ATTR_MODE,
+	NL802154_KEY_ID_ATTR_INDEX,
+	NL802154_KEY_ID_ATTR_IMPLICIT,
+	NL802154_KEY_ID_ATTR_SOURCE_SHORT,
+	NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
+
+	/* keep last */
+	__NL802154_KEY_ID_ATTR_AFTER_LAST,
+	NL802154_KEY_ID_ATTR_MAX = __NL802154_KEY_ID_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_seclevels {
+	NL802154_SECLEVEL_NONE,
+	NL802154_SECLEVEL_MIC32,
+	NL802154_SECLEVEL_MIC64,
+	NL802154_SECLEVEL_MIC128,
+	NL802154_SECLEVEL_ENC,
+	NL802154_SECLEVEL_ENC_MIC32,
+	NL802154_SECLEVEL_ENC_MIC64,
+	NL802154_SECLEVEL_ENC_MIC128,
+
+	/* keep last */
+	__NL802154_SECLEVEL_AFTER_LAST,
+	NL802154_SECLEVEL_MAX = __NL802154_SECLEVEL_AFTER_LAST - 1
+};
+
+enum nl802154_frames {
+	NL802154_FRAME_BEACON,
+	NL802154_FRAME_DATA,
+	NL802154_FRAME_ACK,
+	NL802154_FRAME_CMD,
+
+	/* keep last */
+	__NL802154_FRAME_AFTER_LAST,
+	NL802154_FRAME_MAX = __NL802154_FRAME_AFTER_LAST - 1
+};
+
+enum nl802154_cmd_frames {
+	__NL802154_CMD_FRAME_INVALID,
+	NL802154_CMD_FRAME_ASSOC_REQUEST,
+	NL802154_CMD_FRAME_ASSOC_RESPONSE,
+	NL802154_CMD_FRAME_DISASSOC_NOTIFY,
+	NL802154_CMD_FRAME_DATA_REQUEST,
+	NL802154_CMD_FRAME_PAN_ID_CONFLICT_NOTIFY,
+	NL802154_CMD_FRAME_ORPHAN_NOTIFY,
+	NL802154_CMD_FRAME_BEACON_REQUEST,
+	NL802154_CMD_FRAME_COORD_REALIGNMENT,
+	NL802154_CMD_FRAME_GTS_REQUEST,
+
+	/* keep last */
+	__NL802154_CMD_FRAME_AFTER_LAST,
+	NL802154_CMD_FRAME_MAX = __NL802154_CMD_FRAME_AFTER_LAST - 1
+};
+
+enum nl802154_seclevel_attrs {
+	NL802154_SECLEVEL_ATTR_UNSPEC,
+
+	NL802154_SECLEVEL_ATTR_LEVELS,
+	NL802154_SECLEVEL_ATTR_FRAME,
+	NL802154_SECLEVEL_ATTR_CMD_FRAME,
+	NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
+
+	/* keep last */
+	__NL802154_SECLEVEL_ATTR_AFTER_LAST,
+	NL802154_SECLEVEL_ATTR_MAX = __NL802154_SECLEVEL_ATTR_AFTER_LAST - 1
+};
+
+/* TODO what is this? couldn't find in mib */
+enum {
+	NL802154_DEVKEY_IGNORE,
+	NL802154_DEVKEY_RESTRICT,
+	NL802154_DEVKEY_RECORD,
+
+	/* keep last */
+	__NL802154_DEVKEY_AFTER_LAST,
+	NL802154_DEVKEY_MAX = __NL802154_DEVKEY_AFTER_LAST - 1
+};
+
+enum nl802154_dev {
+	NL802154_DEV_ATTR_UNSPEC,
+
+	NL802154_DEV_ATTR_FRAME_COUNTER,
+	NL802154_DEV_ATTR_PAN_ID,
+	NL802154_DEV_ATTR_SHORT_ADDR,
+	NL802154_DEV_ATTR_EXTENDED_ADDR,
+	NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
+	NL802154_DEV_ATTR_KEY_MODE,
+
+	/* keep last */
+	__NL802154_DEV_ATTR_AFTER_LAST,
+	NL802154_DEV_ATTR_MAX = __NL802154_DEV_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_devkey {
+	NL802154_DEVKEY_ATTR_UNSPEC,
+
+	NL802154_DEVKEY_ATTR_FRAME_COUNTER,
+	NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
+	NL802154_DEVKEY_ATTR_ID,
+
+	/* keep last */
+	__NL802154_DEVKEY_ATTR_AFTER_LAST,
+	NL802154_DEVKEY_ATTR_MAX = __NL802154_DEVKEY_ATTR_AFTER_LAST - 1
+};
+
+enum nl802154_key {
+	NL802154_KEY_ATTR_UNSPEC,
+
+	NL802154_KEY_ATTR_ID,
+	NL802154_KEY_ATTR_USAGE_FRAMES,
+	NL802154_KEY_ATTR_USAGE_CMDS,
+	NL802154_KEY_ATTR_BYTES,
+
+	/* keep last */
+	__NL802154_KEY_ATTR_AFTER_LAST,
+	NL802154_KEY_ATTR_MAX = __NL802154_KEY_ATTR_AFTER_LAST - 1
+};
+
+#define NL802154_KEY_SIZE		16
+#define NL802154_CMD_FRAME_NR_IDS	256
+
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 #endif /* __NL802154_H */
 #endif /* __NL802154_H */

+ 5 - 0
net/6lowpan/core.c

@@ -17,6 +17,11 @@
 
 
 void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
 void lowpan_netdev_setup(struct net_device *dev, enum lowpan_lltypes lltype)
 {
 {
+	dev->addr_len = EUI64_ADDR_LEN;
+	dev->type = ARPHRD_6LOWPAN;
+	dev->mtu = IPV6_MIN_MTU;
+	dev->priv_flags |= IFF_NO_QUEUE;
+
 	lowpan_priv(dev)->lltype = lltype;
 	lowpan_priv(dev)->lltype = lltype;
 }
 }
 EXPORT_SYMBOL(lowpan_netdev_setup);
 EXPORT_SYMBOL(lowpan_netdev_setup);

+ 0 - 2
net/6lowpan/nhc.h

@@ -8,8 +8,6 @@
 #include <net/6lowpan.h>
 #include <net/6lowpan.h>
 #include <net/ipv6.h>
 #include <net/ipv6.h>
 
 
-#define LOWPAN_NHC_MAX_ID_LEN	1
-
 /**
 /**
  * LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
  * LOWPAN_NHC - helper macro to generate nh id fields and lowpan_nhc struct
  *
  *

+ 24 - 54
net/bluetooth/6lowpan.c

@@ -35,7 +35,6 @@ static struct dentry *lowpan_enable_debugfs;
 static struct dentry *lowpan_control_debugfs;
 static struct dentry *lowpan_control_debugfs;
 
 
 #define IFACE_NAME_TEMPLATE "bt%d"
 #define IFACE_NAME_TEMPLATE "bt%d"
-#define EUI64_ADDR_LEN 8
 
 
 struct skb_cb {
 struct skb_cb {
 	struct in6_addr addr;
 	struct in6_addr addr;
@@ -674,13 +673,8 @@ static struct header_ops header_ops = {
 
 
 static void netdev_setup(struct net_device *dev)
 static void netdev_setup(struct net_device *dev)
 {
 {
-	dev->addr_len		= EUI64_ADDR_LEN;
-	dev->type		= ARPHRD_6LOWPAN;
-
 	dev->hard_header_len	= 0;
 	dev->hard_header_len	= 0;
 	dev->needed_tailroom	= 0;
 	dev->needed_tailroom	= 0;
-	dev->mtu		= IPV6_MIN_MTU;
-	dev->tx_queue_len	= 0;
 	dev->flags		= IFF_RUNNING | IFF_POINTOPOINT |
 	dev->flags		= IFF_RUNNING | IFF_POINTOPOINT |
 				  IFF_MULTICAST;
 				  IFF_MULTICAST;
 	dev->watchdog_timeo	= 0;
 	dev->watchdog_timeo	= 0;
@@ -775,24 +769,7 @@ static struct l2cap_chan *chan_create(void)
 
 
 	chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
 	chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
 	chan->mode = L2CAP_MODE_LE_FLOWCTL;
 	chan->mode = L2CAP_MODE_LE_FLOWCTL;
-	chan->omtu = 65535;
-	chan->imtu = chan->omtu;
-
-	return chan;
-}
-
-static struct l2cap_chan *chan_open(struct l2cap_chan *pchan)
-{
-	struct l2cap_chan *chan;
-
-	chan = chan_create();
-	if (!chan)
-		return NULL;
-
-	chan->remote_mps = chan->omtu;
-	chan->mps = chan->omtu;
-
-	chan->state = BT_CONNECTED;
+	chan->imtu = 1280;
 
 
 	return chan;
 	return chan;
 }
 }
@@ -919,7 +896,10 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
 {
 {
 	struct l2cap_chan *chan;
 	struct l2cap_chan *chan;
 
 
-	chan = chan_open(pchan);
+	chan = chan_create();
+	if (!chan)
+		return NULL;
+
 	chan->ops = pchan->ops;
 	chan->ops = pchan->ops;
 
 
 	BT_DBG("chan %p pchan %p", chan, pchan);
 	BT_DBG("chan %p pchan %p", chan, pchan);
@@ -1065,34 +1045,23 @@ static inline __u8 bdaddr_type(__u8 type)
 		return BDADDR_LE_RANDOM;
 		return BDADDR_LE_RANDOM;
 }
 }
 
 
-static struct l2cap_chan *chan_get(void)
-{
-	struct l2cap_chan *pchan;
-
-	pchan = chan_create();
-	if (!pchan)
-		return NULL;
-
-	pchan->ops = &bt_6lowpan_chan_ops;
-
-	return pchan;
-}
-
 static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
 static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type)
 {
 {
-	struct l2cap_chan *pchan;
+	struct l2cap_chan *chan;
 	int err;
 	int err;
 
 
-	pchan = chan_get();
-	if (!pchan)
+	chan = chan_create();
+	if (!chan)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	err = l2cap_chan_connect(pchan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
+	chan->ops = &bt_6lowpan_chan_ops;
+
+	err = l2cap_chan_connect(chan, cpu_to_le16(L2CAP_PSM_IPSP), 0,
 				 addr, dst_type);
 				 addr, dst_type);
 
 
-	BT_DBG("chan %p err %d", pchan, err);
+	BT_DBG("chan %p err %d", chan, err);
 	if (err < 0)
 	if (err < 0)
-		l2cap_chan_put(pchan);
+		l2cap_chan_put(chan);
 
 
 	return err;
 	return err;
 }
 }
@@ -1117,31 +1086,32 @@ static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type)
 static struct l2cap_chan *bt_6lowpan_listen(void)
 static struct l2cap_chan *bt_6lowpan_listen(void)
 {
 {
 	bdaddr_t *addr = BDADDR_ANY;
 	bdaddr_t *addr = BDADDR_ANY;
-	struct l2cap_chan *pchan;
+	struct l2cap_chan *chan;
 	int err;
 	int err;
 
 
 	if (!enable_6lowpan)
 	if (!enable_6lowpan)
 		return NULL;
 		return NULL;
 
 
-	pchan = chan_get();
-	if (!pchan)
+	chan = chan_create();
+	if (!chan)
 		return NULL;
 		return NULL;
 
 
-	pchan->state = BT_LISTEN;
-	pchan->src_type = BDADDR_LE_PUBLIC;
+	chan->ops = &bt_6lowpan_chan_ops;
+	chan->state = BT_LISTEN;
+	chan->src_type = BDADDR_LE_PUBLIC;
 
 
-	atomic_set(&pchan->nesting, L2CAP_NESTING_PARENT);
+	atomic_set(&chan->nesting, L2CAP_NESTING_PARENT);
 
 
-	BT_DBG("chan %p src type %d", pchan, pchan->src_type);
+	BT_DBG("chan %p src type %d", chan, chan->src_type);
 
 
-	err = l2cap_add_psm(pchan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
+	err = l2cap_add_psm(chan, addr, cpu_to_le16(L2CAP_PSM_IPSP));
 	if (err) {
 	if (err) {
-		l2cap_chan_put(pchan);
+		l2cap_chan_put(chan);
 		BT_ERR("psm cannot be added err %d", err);
 		BT_ERR("psm cannot be added err %d", err);
 		return NULL;
 		return NULL;
 	}
 	}
 
 
-	return pchan;
+	return chan;
 }
 }
 
 
 static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
 static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,

+ 128 - 14
net/bluetooth/hci_core.c

@@ -134,6 +134,66 @@ static const struct file_operations dut_mode_fops = {
 	.llseek		= default_llseek,
 	.llseek		= default_llseek,
 };
 };
 
 
+static ssize_t vendor_diag_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] = hci_dev_test_flag(hdev, HCI_VENDOR_DIAG) ? 'Y': 'N';
+	buf[1] = '\n';
+	buf[2] = '\0';
+	return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t vendor_diag_write(struct file *file, const char __user *user_buf,
+				 size_t count, loff_t *ppos)
+{
+	struct hci_dev *hdev = file->private_data;
+	char buf[32];
+	size_t buf_size = min(count, (sizeof(buf)-1));
+	bool enable;
+	int err;
+
+	if (copy_from_user(buf, user_buf, buf_size))
+		return -EFAULT;
+
+	buf[buf_size] = '\0';
+	if (strtobool(buf, &enable))
+		return -EINVAL;
+
+	hci_req_lock(hdev);
+	err = hdev->set_diag(hdev, enable);
+	hci_req_unlock(hdev);
+
+	if (err < 0)
+		return err;
+
+	if (enable)
+		hci_dev_set_flag(hdev, HCI_VENDOR_DIAG);
+	else
+		hci_dev_clear_flag(hdev, HCI_VENDOR_DIAG);
+
+	return count;
+}
+
+static const struct file_operations vendor_diag_fops = {
+	.open		= simple_open,
+	.read		= vendor_diag_read,
+	.write		= vendor_diag_write,
+	.llseek		= default_llseek,
+};
+
+static void hci_debugfs_create_basic(struct hci_dev *hdev)
+{
+	debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+			    &dut_mode_fops);
+
+	if (hdev->set_diag)
+		debugfs_create_file("vendor_diag", 0644, hdev->debugfs, hdev,
+				    &vendor_diag_fops);
+}
+
 /* ---- HCI requests ---- */
 /* ---- HCI requests ---- */
 
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result, u16 opcode,
@@ -850,13 +910,8 @@ static int __hci_init(struct hci_dev *hdev)
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
-	/* The Device Under Test (DUT) mode is special and available for
-	 * all controller types. So just create it early on.
-	 */
-	if (hci_dev_test_flag(hdev, HCI_SETUP)) {
-		debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
-				    &dut_mode_fops);
-	}
+	if (hci_dev_test_flag(hdev, HCI_SETUP))
+		hci_debugfs_create_basic(hdev);
 
 
 	err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
 	err = __hci_req_sync(hdev, hci_init2_req, 0, HCI_INIT_TIMEOUT);
 	if (err < 0)
 	if (err < 0)
@@ -933,6 +988,9 @@ static int __hci_unconf_init(struct hci_dev *hdev)
 	if (err < 0)
 	if (err < 0)
 		return err;
 		return err;
 
 
+	if (hci_dev_test_flag(hdev, HCI_SETUP))
+		hci_debugfs_create_basic(hdev);
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1385,6 +1443,9 @@ static int hci_dev_do_open(struct hci_dev *hdev)
 		goto done;
 		goto done;
 	}
 	}
 
 
+	set_bit(HCI_RUNNING, &hdev->flags);
+	hci_notify(hdev, HCI_DEV_OPEN);
+
 	atomic_set(&hdev->cmd_cnt, 1);
 	atomic_set(&hdev->cmd_cnt, 1);
 	set_bit(HCI_INIT, &hdev->flags);
 	set_bit(HCI_INIT, &hdev->flags);
 
 
@@ -1466,6 +1527,9 @@ static int hci_dev_do_open(struct hci_dev *hdev)
 			hdev->sent_cmd = NULL;
 			hdev->sent_cmd = NULL;
 		}
 		}
 
 
+		clear_bit(HCI_RUNNING, &hdev->flags);
+		hci_notify(hdev, HCI_DEV_CLOSE);
+
 		hdev->close(hdev);
 		hdev->close(hdev);
 		hdev->flags &= BIT(HCI_RAW);
 		hdev->flags &= BIT(HCI_RAW);
 	}
 	}
@@ -1551,6 +1615,8 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
 
 
 int hci_dev_do_close(struct hci_dev *hdev)
 int hci_dev_do_close(struct hci_dev *hdev)
 {
 {
+	bool auto_off;
+
 	BT_DBG("%s %p", hdev->name, hdev);
 	BT_DBG("%s %p", hdev->name, hdev);
 
 
 	if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
 	if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
@@ -1606,10 +1672,10 @@ int hci_dev_do_close(struct hci_dev *hdev)
 
 
 	hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 	hci_discovery_set_state(hdev, DISCOVERY_STOPPED);
 
 
-	if (!hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
-		if (hdev->dev_type == HCI_BREDR)
-			mgmt_powered(hdev, 0);
-	}
+	auto_off = hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF);
+
+	if (!auto_off && hdev->dev_type == HCI_BREDR)
+		mgmt_powered(hdev, 0);
 
 
 	hci_inquiry_cache_flush(hdev);
 	hci_inquiry_cache_flush(hdev);
 	hci_pend_le_actions_clear(hdev);
 	hci_pend_le_actions_clear(hdev);
@@ -1626,9 +1692,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
 	/* Reset device */
 	/* Reset device */
 	skb_queue_purge(&hdev->cmd_q);
 	skb_queue_purge(&hdev->cmd_q);
 	atomic_set(&hdev->cmd_cnt, 1);
 	atomic_set(&hdev->cmd_cnt, 1);
-	if (!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
-	    !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
-	    test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) {
+	if (test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks) &&
+	    !auto_off && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) {
 		set_bit(HCI_INIT, &hdev->flags);
 		set_bit(HCI_INIT, &hdev->flags);
 		__hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
 		__hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
 		clear_bit(HCI_INIT, &hdev->flags);
 		clear_bit(HCI_INIT, &hdev->flags);
@@ -1649,6 +1714,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
 		hdev->sent_cmd = NULL;
 		hdev->sent_cmd = NULL;
 	}
 	}
 
 
+	clear_bit(HCI_RUNNING, &hdev->flags);
+	hci_notify(hdev, HCI_DEV_CLOSE);
+
 	/* After this point our queues are empty
 	/* After this point our queues are empty
 	 * and no tasks are scheduled. */
 	 * and no tasks are scheduled. */
 	hdev->close(hdev);
 	hdev->close(hdev);
@@ -3471,6 +3539,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 		return -ENXIO;
 		return -ENXIO;
 	}
 	}
 
 
+	if (bt_cb(skb)->pkt_type != HCI_EVENT_PKT &&
+	    bt_cb(skb)->pkt_type != HCI_ACLDATA_PKT &&
+	    bt_cb(skb)->pkt_type != HCI_SCODATA_PKT) {
+		kfree_skb(skb);
+		return -EINVAL;
+	}
+
 	/* Incoming skb */
 	/* Incoming skb */
 	bt_cb(skb)->incoming = 1;
 	bt_cb(skb)->incoming = 1;
 
 
@@ -3484,6 +3559,21 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
 }
 }
 EXPORT_SYMBOL(hci_recv_frame);
 EXPORT_SYMBOL(hci_recv_frame);
 
 
+/* Receive diagnostic message from HCI drivers */
+int hci_recv_diag(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	/* Time stamp */
+	__net_timestamp(skb);
+
+	/* Mark as diagnostic packet and send to monitor */
+	bt_cb(skb)->pkt_type = HCI_DIAG_PKT;
+	hci_send_to_monitor(hdev, skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+EXPORT_SYMBOL(hci_recv_diag);
+
 /* ---- Interface to upper protocols ---- */
 /* ---- Interface to upper protocols ---- */
 
 
 int hci_register_cb(struct hci_cb *cb)
 int hci_register_cb(struct hci_cb *cb)
@@ -3530,6 +3620,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 	/* Get rid of skb owner, prior to sending to the driver. */
 	/* Get rid of skb owner, prior to sending to the driver. */
 	skb_orphan(skb);
 	skb_orphan(skb);
 
 
+	if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+		kfree_skb(skb);
+		return;
+	}
+
 	err = hdev->send(hdev, skb);
 	err = hdev->send(hdev, skb);
 	if (err < 0) {
 	if (err < 0) {
 		BT_ERR("%s sending frame failed (%d)", hdev->name, err);
 		BT_ERR("%s sending frame failed (%d)", hdev->name, err);
@@ -3580,6 +3675,25 @@ void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
 	return hdev->sent_cmd->data + HCI_COMMAND_HDR_SIZE;
 }
 }
 
 
+/* Send HCI command and wait for command commplete event */
+struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+			     const void *param, u32 timeout)
+{
+	struct sk_buff *skb;
+
+	if (!test_bit(HCI_UP, &hdev->flags))
+		return ERR_PTR(-ENETDOWN);
+
+	bt_dev_dbg(hdev, "opcode 0x%4.4x plen %d", opcode, plen);
+
+	hci_req_lock(hdev);
+	skb = __hci_cmd_sync(hdev, opcode, plen, param, timeout);
+	hci_req_unlock(hdev);
+
+	return skb;
+}
+EXPORT_SYMBOL(hci_cmd_sync);
+
 /* Send ACL data */
 /* Send ACL data */
 static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
 static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
 {
 {

+ 62 - 8
net/bluetooth/hci_sock.c

@@ -279,6 +279,9 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
 		else
 		else
 			opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
 			opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
 		break;
 		break;
+	case HCI_DIAG_PKT:
+		opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
+		break;
 	default:
 	default:
 		return;
 		return;
 	}
 	}
@@ -303,6 +306,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
 {
 {
 	struct hci_mon_hdr *hdr;
 	struct hci_mon_hdr *hdr;
 	struct hci_mon_new_index *ni;
 	struct hci_mon_new_index *ni;
+	struct hci_mon_index_info *ii;
 	struct sk_buff *skb;
 	struct sk_buff *skb;
 	__le16 opcode;
 	__le16 opcode;
 
 
@@ -312,7 +316,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
 		if (!skb)
 		if (!skb)
 			return NULL;
 			return NULL;
 
 
-		ni = (void *) skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
+		ni = (void *)skb_put(skb, HCI_MON_NEW_INDEX_SIZE);
 		ni->type = hdev->dev_type;
 		ni->type = hdev->dev_type;
 		ni->bus = hdev->bus;
 		ni->bus = hdev->bus;
 		bacpy(&ni->bdaddr, &hdev->bdaddr);
 		bacpy(&ni->bdaddr, &hdev->bdaddr);
@@ -329,6 +333,34 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
 		opcode = cpu_to_le16(HCI_MON_DEL_INDEX);
 		opcode = cpu_to_le16(HCI_MON_DEL_INDEX);
 		break;
 		break;
 
 
+	case HCI_DEV_UP:
+		skb = bt_skb_alloc(HCI_MON_INDEX_INFO_SIZE, GFP_ATOMIC);
+		if (!skb)
+			return NULL;
+
+		ii = (void *)skb_put(skb, HCI_MON_INDEX_INFO_SIZE);
+		bacpy(&ii->bdaddr, &hdev->bdaddr);
+		ii->manufacturer = cpu_to_le16(hdev->manufacturer);
+
+		opcode = cpu_to_le16(HCI_MON_INDEX_INFO);
+		break;
+
+	case HCI_DEV_OPEN:
+		skb = bt_skb_alloc(0, GFP_ATOMIC);
+		if (!skb)
+			return NULL;
+
+		opcode = cpu_to_le16(HCI_MON_OPEN_INDEX);
+		break;
+
+	case HCI_DEV_CLOSE:
+		skb = bt_skb_alloc(0, GFP_ATOMIC);
+		if (!skb)
+			return NULL;
+
+		opcode = cpu_to_le16(HCI_MON_CLOSE_INDEX);
+		break;
+
 	default:
 	default:
 		return NULL;
 		return NULL;
 	}
 	}
@@ -358,6 +390,26 @@ static void send_monitor_replay(struct sock *sk)
 
 
 		if (sock_queue_rcv_skb(sk, skb))
 		if (sock_queue_rcv_skb(sk, skb))
 			kfree_skb(skb);
 			kfree_skb(skb);
+
+		if (!test_bit(HCI_RUNNING, &hdev->flags))
+			continue;
+
+		skb = create_monitor_event(hdev, HCI_DEV_OPEN);
+		if (!skb)
+			continue;
+
+		if (sock_queue_rcv_skb(sk, skb))
+			kfree_skb(skb);
+
+		if (!test_bit(HCI_UP, &hdev->flags))
+			continue;
+
+		skb = create_monitor_event(hdev, HCI_DEV_UP);
+		if (!skb)
+			continue;
+
+		if (sock_queue_rcv_skb(sk, skb))
+			kfree_skb(skb);
 	}
 	}
 
 
 	read_unlock(&hci_dev_list_lock);
 	read_unlock(&hci_dev_list_lock);
@@ -392,14 +444,12 @@ static void hci_si_event(struct hci_dev *hdev, int type, int dlen, void *data)
 
 
 void hci_sock_dev_event(struct hci_dev *hdev, int event)
 void hci_sock_dev_event(struct hci_dev *hdev, int event)
 {
 {
-	struct hci_ev_si_device ev;
-
 	BT_DBG("hdev %s event %d", hdev->name, event);
 	BT_DBG("hdev %s event %d", hdev->name, event);
 
 
-	/* Send event to monitor */
 	if (atomic_read(&monitor_promisc)) {
 	if (atomic_read(&monitor_promisc)) {
 		struct sk_buff *skb;
 		struct sk_buff *skb;
 
 
+		/* Send event to monitor */
 		skb = create_monitor_event(hdev, event);
 		skb = create_monitor_event(hdev, event);
 		if (skb) {
 		if (skb) {
 			hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
 			hci_send_to_channel(HCI_CHANNEL_MONITOR, skb,
@@ -408,10 +458,14 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event)
 		}
 		}
 	}
 	}
 
 
-	/* Send event to sockets */
-	ev.event  = event;
-	ev.dev_id = hdev->id;
-	hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
+	if (event <= HCI_DEV_DOWN) {
+		struct hci_ev_si_device ev;
+
+		/* Send event to sockets */
+		ev.event  = event;
+		ev.dev_id = hdev->id;
+		hci_si_event(NULL, HCI_EV_SI_DEVICE, sizeof(ev), &ev);
+	}
 
 
 	if (event == HCI_DEV_UNREG) {
 	if (event == HCI_DEV_UNREG) {
 		struct sock *sk;
 		struct sock *sk;

+ 16 - 0
net/bluetooth/lib.c

@@ -151,6 +151,22 @@ void bt_info(const char *format, ...)
 }
 }
 EXPORT_SYMBOL(bt_info);
 EXPORT_SYMBOL(bt_info);
 
 
+void bt_warn(const char *format, ...)
+{
+	struct va_format vaf;
+	va_list args;
+
+	va_start(args, format);
+
+	vaf.fmt = format;
+	vaf.va = &args;
+
+	pr_warn("%pV", &vaf);
+
+	va_end(args);
+}
+EXPORT_SYMBOL(bt_warn);
+
 void bt_err(const char *format, ...)
 void bt_err(const char *format, ...)
 {
 {
 	struct va_format vaf;
 	struct va_format vaf;

+ 11 - 7
net/ieee802154/6lowpan/core.c

@@ -101,14 +101,9 @@ static const struct net_device_ops lowpan_netdev_ops = {
 
 
 static void lowpan_setup(struct net_device *ldev)
 static void lowpan_setup(struct net_device *ldev)
 {
 {
-	ldev->addr_len		= IEEE802154_ADDR_LEN;
 	memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
 	memset(ldev->broadcast, 0xff, IEEE802154_ADDR_LEN);
-	ldev->type		= ARPHRD_6LOWPAN;
-	/* Frame Control + Sequence Number + Address fields + Security Header */
-	ldev->hard_header_len	= 2 + 1 + 20 + 14;
-	ldev->needed_tailroom	= 2; /* FCS */
-	ldev->mtu		= IPV6_MIN_MTU;
-	ldev->priv_flags	|= IFF_NO_QUEUE;
+	/* We need an ipv6hdr as minimum len when calling xmit */
+	ldev->hard_header_len	= sizeof(struct ipv6hdr);
 	ldev->flags		= IFF_BROADCAST | IFF_MULTICAST;
 	ldev->flags		= IFF_BROADCAST | IFF_MULTICAST;
 
 
 	ldev->netdev_ops	= &lowpan_netdev_ops;
 	ldev->netdev_ops	= &lowpan_netdev_ops;
@@ -156,6 +151,15 @@ static int lowpan_newlink(struct net *src_net, struct net_device *ldev,
 	lowpan_dev_info(ldev)->wdev = wdev;
 	lowpan_dev_info(ldev)->wdev = wdev;
 	/* Set the lowpan hardware address to the wpan hardware address. */
 	/* Set the lowpan hardware address to the wpan hardware address. */
 	memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN);
 	memcpy(ldev->dev_addr, wdev->dev_addr, IEEE802154_ADDR_LEN);
+	/* We need headroom for possible wpan_dev_hard_header call and tailroom
+	 * for encryption/fcs handling. The lowpan interface will replace
+	 * the IPv6 header with 6LoWPAN header. At worst case the 6LoWPAN
+	 * header has LOWPAN_IPHC_MAX_HEADER_LEN more bytes than the IPv6
+	 * header.
+	 */
+	ldev->needed_headroom = LOWPAN_IPHC_MAX_HEADER_LEN +
+				wdev->needed_headroom;
+	ldev->needed_tailroom = wdev->needed_tailroom;
 
 
 	lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
 	lowpan_netdev_setup(ldev, LOWPAN_LLTYPE_IEEE802154);
 
 

+ 2 - 0
net/ieee802154/6lowpan/rx.c

@@ -29,6 +29,8 @@
 static int lowpan_give_skb_to_device(struct sk_buff *skb)
 static int lowpan_give_skb_to_device(struct sk_buff *skb)
 {
 {
 	skb->protocol = htons(ETH_P_IPV6);
 	skb->protocol = htons(ETH_P_IPV6);
+	skb->dev->stats.rx_packets++;
+	skb->dev->stats.rx_bytes += skb->len;
 
 
 	return netif_rx(skb);
 	return netif_rx(skb);
 }
 }

+ 35 - 14
net/ieee802154/6lowpan/tx.c

@@ -10,6 +10,7 @@
 
 
 #include <net/6lowpan.h>
 #include <net/6lowpan.h>
 #include <net/ieee802154_netdev.h>
 #include <net/ieee802154_netdev.h>
+#include <net/mac802154.h>
 
 
 #include "6lowpan_i.h"
 #include "6lowpan_i.h"
 
 
@@ -36,6 +37,13 @@ lowpan_addr_info *lowpan_skb_priv(const struct sk_buff *skb)
 			sizeof(struct lowpan_addr_info));
 			sizeof(struct lowpan_addr_info));
 }
 }
 
 
+/* This callback will be called from AF_PACKET and IPv6 stack, the AF_PACKET
+ * sockets gives an 8 byte array for addresses only!
+ *
+ * TODO I think AF_PACKET DGRAM (sending/receiving) RAW (sending) makes no
+ * sense here. We should disable it, the right use-case would be AF_INET6
+ * RAW/DGRAM sockets.
+ */
 int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
 int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
 			 unsigned short type, const void *_daddr,
 			 unsigned short type, const void *_daddr,
 			 const void *_saddr, unsigned int len)
 			 const void *_saddr, unsigned int len)
@@ -71,27 +79,33 @@ int lowpan_header_create(struct sk_buff *skb, struct net_device *ldev,
 
 
 static struct sk_buff*
 static struct sk_buff*
 lowpan_alloc_frag(struct sk_buff *skb, int size,
 lowpan_alloc_frag(struct sk_buff *skb, int size,
-		  const struct ieee802154_hdr *master_hdr)
+		  const struct ieee802154_hdr *master_hdr, bool frag1)
 {
 {
 	struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev;
 	struct net_device *wdev = lowpan_dev_info(skb->dev)->wdev;
 	struct sk_buff *frag;
 	struct sk_buff *frag;
 	int rc;
 	int rc;
 
 
-	frag = alloc_skb(wdev->hard_header_len + wdev->needed_tailroom + size,
+	frag = alloc_skb(wdev->needed_headroom + wdev->needed_tailroom + size,
 			 GFP_ATOMIC);
 			 GFP_ATOMIC);
 
 
 	if (likely(frag)) {
 	if (likely(frag)) {
 		frag->dev = wdev;
 		frag->dev = wdev;
 		frag->priority = skb->priority;
 		frag->priority = skb->priority;
-		skb_reserve(frag, wdev->hard_header_len);
+		skb_reserve(frag, wdev->needed_headroom);
 		skb_reset_network_header(frag);
 		skb_reset_network_header(frag);
 		*mac_cb(frag) = *mac_cb(skb);
 		*mac_cb(frag) = *mac_cb(skb);
 
 
-		rc = dev_hard_header(frag, wdev, 0, &master_hdr->dest,
-				     &master_hdr->source, size);
-		if (rc < 0) {
-			kfree_skb(frag);
-			return ERR_PTR(rc);
+		if (frag1) {
+			memcpy(skb_put(frag, skb->mac_len),
+			       skb_mac_header(skb), skb->mac_len);
+		} else {
+			rc = wpan_dev_hard_header(frag, wdev,
+						  &master_hdr->dest,
+						  &master_hdr->source, size);
+			if (rc < 0) {
+				kfree_skb(frag);
+				return ERR_PTR(rc);
+			}
 		}
 		}
 	} else {
 	} else {
 		frag = ERR_PTR(-ENOMEM);
 		frag = ERR_PTR(-ENOMEM);
@@ -103,13 +117,13 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
 static int
 static int
 lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
 lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
 		     u8 *frag_hdr, int frag_hdrlen,
 		     u8 *frag_hdr, int frag_hdrlen,
-		     int offset, int len)
+		     int offset, int len, bool frag1)
 {
 {
 	struct sk_buff *frag;
 	struct sk_buff *frag;
 
 
 	raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
 	raw_dump_inline(__func__, " fragment header", frag_hdr, frag_hdrlen);
 
 
-	frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr);
+	frag = lowpan_alloc_frag(skb, frag_hdrlen + len, wpan_hdr, frag1);
 	if (IS_ERR(frag))
 	if (IS_ERR(frag))
 		return PTR_ERR(frag);
 		return PTR_ERR(frag);
 
 
@@ -148,7 +162,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
 
 
 	rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
 	rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
 				  LOWPAN_FRAG1_HEAD_SIZE, 0,
 				  LOWPAN_FRAG1_HEAD_SIZE, 0,
-				  frag_len + skb_network_header_len(skb));
+				  frag_len + skb_network_header_len(skb),
+				  true);
 	if (rc) {
 	if (rc) {
 		pr_debug("%s unable to send FRAG1 packet (tag: %d)",
 		pr_debug("%s unable to send FRAG1 packet (tag: %d)",
 			 __func__, ntohs(frag_tag));
 			 __func__, ntohs(frag_tag));
@@ -169,7 +184,7 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
 
 
 		rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
 		rc = lowpan_xmit_fragment(skb, wpan_hdr, frag_hdr,
 					  LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
 					  LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
-					  frag_len);
+					  frag_len, false);
 		if (rc) {
 		if (rc) {
 			pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
 			pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
 				 __func__, ntohs(frag_tag), skb_offset);
 				 __func__, ntohs(frag_tag), skb_offset);
@@ -177,6 +192,8 @@ lowpan_xmit_fragmented(struct sk_buff *skb, struct net_device *ldev,
 		}
 		}
 	} while (skb_unprocessed > frag_cap);
 	} while (skb_unprocessed > frag_cap);
 
 
+	ldev->stats.tx_packets++;
+	ldev->stats.tx_bytes += dgram_size;
 	consume_skb(skb);
 	consume_skb(skb);
 	return NET_XMIT_SUCCESS;
 	return NET_XMIT_SUCCESS;
 
 
@@ -228,8 +245,8 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *ldev,
 		cb->ackreq = wpan_dev->ackreq;
 		cb->ackreq = wpan_dev->ackreq;
 	}
 	}
 
 
-	return dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, ETH_P_IPV6,
-			       (void *)&da, (void *)&sa, 0);
+	return wpan_dev_hard_header(skb, lowpan_dev_info(ldev)->wdev, &da, &sa,
+				    0);
 }
 }
 
 
 netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
@@ -240,6 +257,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 
 
 	pr_debug("package xmit\n");
 	pr_debug("package xmit\n");
 
 
+	WARN_ON_ONCE(skb->len > IPV6_MIN_MTU);
+
 	/* We must take a copy of the skb before we modify/replace the ipv6
 	/* We must take a copy of the skb before we modify/replace the ipv6
 	 * header as the header could be used elsewhere
 	 * header as the header could be used elsewhere
 	 */
 	 */
@@ -262,6 +281,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 
 
 	if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
 	if (skb_tail_pointer(skb) - skb_network_header(skb) <= max_single) {
 		skb->dev = lowpan_dev_info(ldev)->wdev;
 		skb->dev = lowpan_dev_info(ldev)->wdev;
+		ldev->stats.tx_packets++;
+		ldev->stats.tx_bytes += dgram_size;
 		return dev_queue_xmit(skb);
 		return dev_queue_xmit(skb);
 	} else {
 	} else {
 		netdev_tx_t rc;
 		netdev_tx_t rc;

+ 5 - 0
net/ieee802154/Kconfig

@@ -12,6 +12,11 @@ menuconfig IEEE802154
 
 
 if IEEE802154
 if IEEE802154
 
 
+config IEEE802154_NL802154_EXPERIMENTAL
+	bool "IEEE 802.15.4 experimental netlink support"
+	---help---
+	  Adds experimental netlink support for nl802154.
+
 config IEEE802154_SOCKET
 config IEEE802154_SOCKET
 	tristate "IEEE 802.15.4 socket interface"
 	tristate "IEEE 802.15.4 socket interface"
 	default y
 	default y

+ 12 - 0
net/ieee802154/core.c

@@ -95,6 +95,18 @@ cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
 	return result;
 	return result;
 }
 }
 
 
+struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx)
+{
+	struct cfg802154_registered_device *rdev;
+
+	ASSERT_RTNL();
+
+	rdev = cfg802154_rdev_by_wpan_phy_idx(wpan_phy_idx);
+	if (!rdev)
+		return NULL;
+	return &rdev->wpan_phy;
+}
+
 struct wpan_phy *
 struct wpan_phy *
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 {
 {

+ 1 - 0
net/ieee802154/core.h

@@ -42,5 +42,6 @@ extern int cfg802154_rdev_list_generation;
 void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
 void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
 struct cfg802154_registered_device *
 struct cfg802154_registered_device *
 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
 cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx);
+struct wpan_phy *wpan_phy_idx_to_wpan_phy(int wpan_phy_idx);
 
 
 #endif /* __IEEE802154_CORE_H */
 #endif /* __IEEE802154_CORE_H */

+ 10 - 10
net/ieee802154/header_ops.c

@@ -83,35 +83,35 @@ ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr)
 }
 }
 
 
 int
 int
-ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
+ieee802154_hdr_push(struct sk_buff *skb, struct ieee802154_hdr *hdr)
 {
 {
-	u8 buf[MAC802154_FRAME_HARD_HEADER_LEN];
+	u8 buf[IEEE802154_MAX_HEADER_LEN];
 	int pos = 2;
 	int pos = 2;
 	int rc;
 	int rc;
-	struct ieee802154_hdr_fc fc = hdr->fc;
+	struct ieee802154_hdr_fc *fc = &hdr->fc;
 
 
 	buf[pos++] = hdr->seq;
 	buf[pos++] = hdr->seq;
 
 
-	fc.dest_addr_mode = hdr->dest.mode;
+	fc->dest_addr_mode = hdr->dest.mode;
 
 
 	rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false);
 	rc = ieee802154_hdr_push_addr(buf + pos, &hdr->dest, false);
 	if (rc < 0)
 	if (rc < 0)
 		return -EINVAL;
 		return -EINVAL;
 	pos += rc;
 	pos += rc;
 
 
-	fc.source_addr_mode = hdr->source.mode;
+	fc->source_addr_mode = hdr->source.mode;
 
 
 	if (hdr->source.pan_id == hdr->dest.pan_id &&
 	if (hdr->source.pan_id == hdr->dest.pan_id &&
 	    hdr->dest.mode != IEEE802154_ADDR_NONE)
 	    hdr->dest.mode != IEEE802154_ADDR_NONE)
-		fc.intra_pan = true;
+		fc->intra_pan = true;
 
 
-	rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc.intra_pan);
+	rc = ieee802154_hdr_push_addr(buf + pos, &hdr->source, fc->intra_pan);
 	if (rc < 0)
 	if (rc < 0)
 		return -EINVAL;
 		return -EINVAL;
 	pos += rc;
 	pos += rc;
 
 
-	if (fc.security_enabled) {
-		fc.version = 1;
+	if (fc->security_enabled) {
+		fc->version = 1;
 
 
 		rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec);
 		rc = ieee802154_hdr_push_sechdr(buf + pos, &hdr->sec);
 		if (rc < 0)
 		if (rc < 0)
@@ -120,7 +120,7 @@ ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
 		pos += rc;
 		pos += rc;
 	}
 	}
 
 
-	memcpy(buf, &fc, 2);
+	memcpy(buf, fc, 2);
 
 
 	memcpy(skb_push(skb, pos), buf, pos);
 	memcpy(skb_push(skb, pos), buf, pos);
 
 

+ 1224 - 97
net/ieee802154/nl802154.c

@@ -232,8 +232,86 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
 	[NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
 	[NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
 
 
 	[NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 },
 	[NL802154_ATTR_ACKREQ_DEFAULT] = { .type = NLA_U8 },
+
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	[NL802154_ATTR_SEC_ENABLED] = { .type = NLA_U8, },
+	[NL802154_ATTR_SEC_OUT_LEVEL] = { .type = NLA_U32, },
+	[NL802154_ATTR_SEC_OUT_KEY_ID] = { .type = NLA_NESTED, },
+	[NL802154_ATTR_SEC_FRAME_COUNTER] = { .type = NLA_U32 },
+
+	[NL802154_ATTR_SEC_LEVEL] = { .type = NLA_NESTED },
+	[NL802154_ATTR_SEC_DEVICE] = { .type = NLA_NESTED },
+	[NL802154_ATTR_SEC_DEVKEY] = { .type = NLA_NESTED },
+	[NL802154_ATTR_SEC_KEY] = { .type = NLA_NESTED },
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 };
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+static int
+nl802154_prepare_wpan_dev_dump(struct sk_buff *skb,
+			       struct netlink_callback *cb,
+			       struct cfg802154_registered_device **rdev,
+			       struct wpan_dev **wpan_dev)
+{
+	int err;
+
+	rtnl_lock();
+
+	if (!cb->args[0]) {
+		err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl802154_fam.hdrsize,
+				  nl802154_fam.attrbuf, nl802154_fam.maxattr,
+				  nl802154_policy);
+		if (err)
+			goto out_unlock;
+
+		*wpan_dev = __cfg802154_wpan_dev_from_attrs(sock_net(skb->sk),
+							    nl802154_fam.attrbuf);
+		if (IS_ERR(*wpan_dev)) {
+			err = PTR_ERR(*wpan_dev);
+			goto out_unlock;
+		}
+		*rdev = wpan_phy_to_rdev((*wpan_dev)->wpan_phy);
+		/* 0 is the first index - add 1 to parse only once */
+		cb->args[0] = (*rdev)->wpan_phy_idx + 1;
+		cb->args[1] = (*wpan_dev)->identifier;
+	} else {
+		/* subtract the 1 again here */
+		struct wpan_phy *wpan_phy = wpan_phy_idx_to_wpan_phy(cb->args[0] - 1);
+		struct wpan_dev *tmp;
+
+		if (!wpan_phy) {
+			err = -ENODEV;
+			goto out_unlock;
+		}
+		*rdev = wpan_phy_to_rdev(wpan_phy);
+		*wpan_dev = NULL;
+
+		list_for_each_entry(tmp, &(*rdev)->wpan_dev_list, list) {
+			if (tmp->identifier == cb->args[1]) {
+				*wpan_dev = tmp;
+				break;
+			}
+		}
+
+		if (!*wpan_dev) {
+			err = -ENODEV;
+			goto out_unlock;
+		}
+	}
+
+	return 0;
+ out_unlock:
+	rtnl_unlock();
+	return err;
+}
+
+static void
+nl802154_finish_wpan_dev_dump(struct cfg802154_registered_device *rdev)
+{
+	rtnl_unlock();
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 /* message building helper */
 /* message building helper */
 static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
 static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
 				    int flags, u8 cmd)
 				    int flags, u8 cmd)
@@ -612,6 +690,107 @@ static inline u64 wpan_dev_id(struct wpan_dev *wpan_dev)
 	       ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
 	       ((u64)wpan_phy_to_rdev(wpan_dev->wpan_phy)->wpan_phy_idx << 32);
 }
 }
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+#include <net/ieee802154_netdev.h>
+
+static int
+ieee802154_llsec_send_key_id(struct sk_buff *msg,
+			     const struct ieee802154_llsec_key_id *desc)
+{
+	struct nlattr *nl_dev_addr;
+
+	if (nla_put_u32(msg, NL802154_KEY_ID_ATTR_MODE, desc->mode))
+		return -ENOBUFS;
+
+	switch (desc->mode) {
+	case NL802154_KEY_ID_MODE_IMPLICIT:
+		nl_dev_addr = nla_nest_start(msg, NL802154_KEY_ID_ATTR_IMPLICIT);
+		if (!nl_dev_addr)
+			return -ENOBUFS;
+
+		if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_PAN_ID,
+				 desc->device_addr.pan_id) ||
+		    nla_put_u32(msg,  NL802154_DEV_ADDR_ATTR_MODE,
+				desc->device_addr.mode))
+			return -ENOBUFS;
+
+		switch (desc->device_addr.mode) {
+		case NL802154_DEV_ADDR_SHORT:
+			if (nla_put_le16(msg, NL802154_DEV_ADDR_ATTR_SHORT,
+					 desc->device_addr.short_addr))
+				return -ENOBUFS;
+			break;
+		case NL802154_DEV_ADDR_EXTENDED:
+			if (nla_put_le64(msg, NL802154_DEV_ADDR_ATTR_EXTENDED,
+					 desc->device_addr.extended_addr))
+				return -ENOBUFS;
+			break;
+		default:
+			/* userspace should handle unknown */
+			break;
+		}
+
+		nla_nest_end(msg, nl_dev_addr);
+		break;
+	case NL802154_KEY_ID_MODE_INDEX:
+		break;
+	case NL802154_KEY_ID_MODE_INDEX_SHORT:
+		/* TODO renmae short_source? */
+		if (nla_put_le32(msg, NL802154_KEY_ID_ATTR_SOURCE_SHORT,
+				 desc->short_source))
+			return -ENOBUFS;
+		break;
+	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+		if (nla_put_le64(msg, NL802154_KEY_ID_ATTR_SOURCE_EXTENDED,
+				 desc->extended_source))
+			return -ENOBUFS;
+		break;
+	default:
+		/* userspace should handle unknown */
+		break;
+	}
+
+	/* TODO key_id to key_idx ? Check naming */
+	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
+		if (nla_put_u8(msg, NL802154_KEY_ID_ATTR_INDEX, desc->id))
+			return -ENOBUFS;
+	}
+
+	return 0;
+}
+
+static int nl802154_get_llsec_params(struct sk_buff *msg,
+				     struct cfg802154_registered_device *rdev,
+				     struct wpan_dev *wpan_dev)
+{
+	struct nlattr *nl_key_id;
+	struct ieee802154_llsec_params params;
+	int ret;
+
+	ret = rdev_get_llsec_params(rdev, wpan_dev, &params);
+	if (ret < 0)
+		return ret;
+
+	if (nla_put_u8(msg, NL802154_ATTR_SEC_ENABLED, params.enabled) ||
+	    nla_put_u32(msg, NL802154_ATTR_SEC_OUT_LEVEL, params.out_level) ||
+	    nla_put_be32(msg, NL802154_ATTR_SEC_FRAME_COUNTER,
+			 params.frame_counter))
+		return -ENOBUFS;
+
+	nl_key_id = nla_nest_start(msg, NL802154_ATTR_SEC_OUT_KEY_ID);
+	if (!nl_key_id)
+		return -ENOBUFS;
+
+	ret = ieee802154_llsec_send_key_id(msg, &params.out_key);
+	if (ret < 0)
+		return ret;
+
+	nla_nest_end(msg, nl_key_id);
+
+	return 0;
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 static int
 static int
 nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
 nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
 		    struct cfg802154_registered_device *rdev,
 		    struct cfg802154_registered_device *rdev,
@@ -663,6 +842,11 @@ nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
 	if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq))
 	if (nla_put_u8(msg, NL802154_ATTR_ACKREQ_DEFAULT, wpan_dev->ackreq))
 		goto nla_put_failure;
 		goto nla_put_failure;
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	if (nl802154_get_llsec_params(msg, rdev, wpan_dev) < 0)
+		goto nla_put_failure;
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 	genlmsg_end(msg, hdr);
 	genlmsg_end(msg, hdr);
 	return 0;
 	return 0;
 
 
@@ -753,10 +937,8 @@ static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
 			return -EINVAL;
 			return -EINVAL;
 	}
 	}
 
 
-	/* TODO add nla_get_le64 to netlink */
 	if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
 	if (info->attrs[NL802154_ATTR_EXTENDED_ADDR])
-		extended_addr = (__force __le64)nla_get_u64(
-				info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
+		extended_addr = nla_get_le64(info->attrs[NL802154_ATTR_EXTENDED_ADDR]);
 
 
 	if (!rdev->ops->add_virtual_intf)
 	if (!rdev->ops->add_virtual_intf)
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
@@ -1075,122 +1257,954 @@ nl802154_set_ackreq_default(struct sk_buff *skb, struct genl_info *info)
 	return rdev_set_ackreq_default(rdev, wpan_dev, ackreq);
 	return rdev_set_ackreq_default(rdev, wpan_dev, ackreq);
 }
 }
 
 
-#define NL802154_FLAG_NEED_WPAN_PHY	0x01
-#define NL802154_FLAG_NEED_NETDEV	0x02
-#define NL802154_FLAG_NEED_RTNL		0x04
-#define NL802154_FLAG_CHECK_NETDEV_UP	0x08
-#define NL802154_FLAG_NEED_NETDEV_UP	(NL802154_FLAG_NEED_NETDEV |\
-					 NL802154_FLAG_CHECK_NETDEV_UP)
-#define NL802154_FLAG_NEED_WPAN_DEV	0x10
-#define NL802154_FLAG_NEED_WPAN_DEV_UP	(NL802154_FLAG_NEED_WPAN_DEV |\
-					 NL802154_FLAG_CHECK_NETDEV_UP)
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+static const struct nla_policy nl802154_dev_addr_policy[NL802154_DEV_ADDR_ATTR_MAX + 1] = {
+	[NL802154_DEV_ADDR_ATTR_PAN_ID] = { .type = NLA_U16 },
+	[NL802154_DEV_ADDR_ATTR_MODE] = { .type = NLA_U32 },
+	[NL802154_DEV_ADDR_ATTR_SHORT] = { .type = NLA_U16 },
+	[NL802154_DEV_ADDR_ATTR_EXTENDED] = { .type = NLA_U64 },
+};
 
 
-static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
-			     struct genl_info *info)
+static int
+ieee802154_llsec_parse_dev_addr(struct nlattr *nla,
+				struct ieee802154_addr *addr)
 {
 {
-	struct cfg802154_registered_device *rdev;
-	struct wpan_dev *wpan_dev;
-	struct net_device *dev;
-	bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+	struct nlattr *attrs[NL802154_DEV_ADDR_ATTR_MAX + 1];
 
 
-	if (rtnl)
-		rtnl_lock();
+	if (!nla || nla_parse_nested(attrs, NL802154_DEV_ADDR_ATTR_MAX, nla,
+				     nl802154_dev_addr_policy))
+		return -EINVAL;
 
 
-	if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
-		rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
-		if (IS_ERR(rdev)) {
-			if (rtnl)
-				rtnl_unlock();
-			return PTR_ERR(rdev);
-		}
-		info->user_ptr[0] = rdev;
-	} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
-		   ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
-		ASSERT_RTNL();
-		wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
-							   info->attrs);
-		if (IS_ERR(wpan_dev)) {
-			if (rtnl)
-				rtnl_unlock();
-			return PTR_ERR(wpan_dev);
-		}
+	if (!attrs[NL802154_DEV_ADDR_ATTR_PAN_ID] &&
+	    !attrs[NL802154_DEV_ADDR_ATTR_MODE] &&
+	    !(attrs[NL802154_DEV_ADDR_ATTR_SHORT] ||
+	      attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]))
+		return -EINVAL;
 
 
-		dev = wpan_dev->netdev;
-		rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+	addr->pan_id = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_PAN_ID]);
+	addr->mode = nla_get_u32(attrs[NL802154_DEV_ADDR_ATTR_MODE]);
+	switch (addr->mode) {
+	case NL802154_DEV_ADDR_SHORT:
+		addr->short_addr = nla_get_le16(attrs[NL802154_DEV_ADDR_ATTR_SHORT]);
+		break;
+	case NL802154_DEV_ADDR_EXTENDED:
+		addr->extended_addr = nla_get_le64(attrs[NL802154_DEV_ADDR_ATTR_EXTENDED]);
+		break;
+	default:
+		return -EINVAL;
+	}
 
 
-		if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
-			if (!dev) {
-				if (rtnl)
-					rtnl_unlock();
-				return -EINVAL;
-			}
+	return 0;
+}
 
 
-			info->user_ptr[1] = dev;
-		} else {
-			info->user_ptr[1] = wpan_dev;
-		}
+static const struct nla_policy nl802154_key_id_policy[NL802154_KEY_ID_ATTR_MAX + 1] = {
+	[NL802154_KEY_ID_ATTR_MODE] = { .type = NLA_U32 },
+	[NL802154_KEY_ID_ATTR_INDEX] = { .type = NLA_U8 },
+	[NL802154_KEY_ID_ATTR_IMPLICIT] = { .type = NLA_NESTED },
+	[NL802154_KEY_ID_ATTR_SOURCE_SHORT] = { .type = NLA_U32 },
+	[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED] = { .type = NLA_U64 },
+};
 
 
-		if (dev) {
-			if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
-			    !netif_running(dev)) {
-				if (rtnl)
-					rtnl_unlock();
-				return -ENETDOWN;
-			}
+static int
+ieee802154_llsec_parse_key_id(struct nlattr *nla,
+			      struct ieee802154_llsec_key_id *desc)
+{
+	struct nlattr *attrs[NL802154_KEY_ID_ATTR_MAX + 1];
 
 
-			dev_hold(dev);
-		}
+	if (!nla || nla_parse_nested(attrs, NL802154_KEY_ID_ATTR_MAX, nla,
+				     nl802154_key_id_policy))
+		return -EINVAL;
 
 
-		info->user_ptr[0] = rdev;
+	if (!attrs[NL802154_KEY_ID_ATTR_MODE])
+		return -EINVAL;
+
+	desc->mode = nla_get_u32(attrs[NL802154_KEY_ID_ATTR_MODE]);
+	switch (desc->mode) {
+	case NL802154_KEY_ID_MODE_IMPLICIT:
+		if (!attrs[NL802154_KEY_ID_ATTR_IMPLICIT])
+			return -EINVAL;
+
+		if (ieee802154_llsec_parse_dev_addr(attrs[NL802154_KEY_ID_ATTR_IMPLICIT],
+						    &desc->device_addr) < 0)
+			return -EINVAL;
+		break;
+	case NL802154_KEY_ID_MODE_INDEX:
+		break;
+	case NL802154_KEY_ID_MODE_INDEX_SHORT:
+		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT])
+			return -EINVAL;
+
+		desc->short_source = nla_get_le32(attrs[NL802154_KEY_ID_ATTR_SOURCE_SHORT]);
+		break;
+	case NL802154_KEY_ID_MODE_INDEX_EXTENDED:
+		if (!attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED])
+			return -EINVAL;
+
+		desc->extended_source = nla_get_le64(attrs[NL802154_KEY_ID_ATTR_SOURCE_EXTENDED]);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	if (desc->mode != NL802154_KEY_ID_MODE_IMPLICIT) {
+		if (!attrs[NL802154_KEY_ID_ATTR_INDEX])
+			return -EINVAL;
+
+		/* TODO change id to idx */
+		desc->id = nla_get_u8(attrs[NL802154_KEY_ID_ATTR_INDEX]);
 	}
 	}
 
 
 	return 0;
 	return 0;
 }
 }
 
 
-static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
-			       struct genl_info *info)
+static int nl802154_set_llsec_params(struct sk_buff *skb,
+				     struct genl_info *info)
 {
 {
-	if (info->user_ptr[1]) {
-		if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
-			struct wpan_dev *wpan_dev = info->user_ptr[1];
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct ieee802154_llsec_params params;
+	u32 changed = 0;
+	int ret;
 
 
-			if (wpan_dev->netdev)
-				dev_put(wpan_dev->netdev);
-		} else {
-			dev_put(info->user_ptr[1]);
+	if (info->attrs[NL802154_ATTR_SEC_ENABLED]) {
+		u8 enabled;
+
+		enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
+		if (enabled != 0 && enabled != 1)
+			return -EINVAL;
+
+		params.enabled = nla_get_u8(info->attrs[NL802154_ATTR_SEC_ENABLED]);
+		changed |= IEEE802154_LLSEC_PARAM_ENABLED;
+	}
+
+	if (info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID]) {
+		ret = ieee802154_llsec_parse_key_id(info->attrs[NL802154_ATTR_SEC_OUT_KEY_ID],
+						    &params.out_key);
+		if (ret < 0)
+			return ret;
+
+		changed |= IEEE802154_LLSEC_PARAM_OUT_KEY;
+	}
+
+	if (info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]) {
+		params.out_level = nla_get_u32(info->attrs[NL802154_ATTR_SEC_OUT_LEVEL]);
+		if (params.out_level > NL802154_SECLEVEL_MAX)
+			return -EINVAL;
+
+		changed |= IEEE802154_LLSEC_PARAM_OUT_LEVEL;
+	}
+
+	if (info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]) {
+		params.frame_counter = nla_get_be32(info->attrs[NL802154_ATTR_SEC_FRAME_COUNTER]);
+		changed |= IEEE802154_LLSEC_PARAM_FRAME_COUNTER;
+	}
+
+	return rdev_set_llsec_params(rdev, wpan_dev, &params, changed);
+}
+
+static int nl802154_send_key(struct sk_buff *msg, u32 cmd, u32 portid,
+			     u32 seq, int flags,
+			     struct cfg802154_registered_device *rdev,
+			     struct net_device *dev,
+			     const struct ieee802154_llsec_key_entry *key)
+{
+	void *hdr;
+	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32];
+	struct nlattr *nl_key, *nl_key_id;
+
+	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+	if (!hdr)
+		return -1;
+
+	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+		goto nla_put_failure;
+
+	nl_key = nla_nest_start(msg, NL802154_ATTR_SEC_KEY);
+	if (!nl_key)
+		goto nla_put_failure;
+
+	nl_key_id = nla_nest_start(msg, NL802154_KEY_ATTR_ID);
+	if (!nl_key_id)
+		goto nla_put_failure;
+
+	if (ieee802154_llsec_send_key_id(msg, &key->id) < 0)
+		goto nla_put_failure;
+
+	nla_nest_end(msg, nl_key_id);
+
+	if (nla_put_u8(msg, NL802154_KEY_ATTR_USAGE_FRAMES,
+		       key->key->frame_types))
+		goto nla_put_failure;
+
+	if (key->key->frame_types & BIT(NL802154_FRAME_CMD)) {
+		/* TODO for each nested */
+		memset(commands, 0, sizeof(commands));
+		commands[7] = key->key->cmd_frame_ids;
+		if (nla_put(msg, NL802154_KEY_ATTR_USAGE_CMDS,
+			    sizeof(commands), commands))
+			goto nla_put_failure;
+	}
+
+	if (nla_put(msg, NL802154_KEY_ATTR_BYTES, NL802154_KEY_SIZE,
+		    key->key->key))
+		goto nla_put_failure;
+
+	nla_nest_end(msg, nl_key);
+	genlmsg_end(msg, hdr);
+
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_key(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct cfg802154_registered_device *rdev = NULL;
+	struct ieee802154_llsec_key_entry *key;
+	struct ieee802154_llsec_table *table;
+	struct wpan_dev *wpan_dev;
+	int err;
+
+	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+	if (err)
+		return err;
+
+	if (!wpan_dev->netdev) {
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	rdev_lock_llsec_table(rdev, wpan_dev);
+	rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+	/* TODO make it like station dump */
+	if (cb->args[2])
+		goto out;
+
+	list_for_each_entry(key, &table->keys, list) {
+		if (nl802154_send_key(skb, NL802154_CMD_NEW_SEC_KEY,
+				      NETLINK_CB(cb->skb).portid,
+				      cb->nlh->nlmsg_seq, NLM_F_MULTI,
+				      rdev, wpan_dev->netdev, key) < 0) {
+			/* TODO */
+			err = -EIO;
+			rdev_unlock_llsec_table(rdev, wpan_dev);
+			goto out_err;
 		}
 		}
 	}
 	}
 
 
-	if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
-		rtnl_unlock();
+	cb->args[2] = 1;
+
+out:
+	rdev_unlock_llsec_table(rdev, wpan_dev);
+	err = skb->len;
+out_err:
+	nl802154_finish_wpan_dev_dump(rdev);
+
+	return err;
 }
 }
 
 
-static const struct genl_ops nl802154_ops[] = {
-	{
-		.cmd = NL802154_CMD_GET_WPAN_PHY,
-		.doit = nl802154_get_wpan_phy,
-		.dumpit = nl802154_dump_wpan_phy,
-		.done = nl802154_dump_wpan_phy_done,
-		.policy = nl802154_policy,
-		/* can be retrieved by unprivileged users */
-		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
-				  NL802154_FLAG_NEED_RTNL,
-	},
-	{
-		.cmd = NL802154_CMD_GET_INTERFACE,
-		.doit = nl802154_get_interface,
-		.dumpit = nl802154_dump_interface,
-		.policy = nl802154_policy,
-		/* can be retrieved by unprivileged users */
-		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
-				  NL802154_FLAG_NEED_RTNL,
-	},
-	{
-		.cmd = NL802154_CMD_NEW_INTERFACE,
-		.doit = nl802154_new_interface,
-		.policy = nl802154_policy,
-		.flags = GENL_ADMIN_PERM,
-		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+static const struct nla_policy nl802154_key_policy[NL802154_KEY_ATTR_MAX + 1] = {
+	[NL802154_KEY_ATTR_ID] = { NLA_NESTED },
+	/* TODO handle it as for_each_nested and NLA_FLAG? */
+	[NL802154_KEY_ATTR_USAGE_FRAMES] = { NLA_U8 },
+	/* TODO handle it as for_each_nested, not static array? */
+	[NL802154_KEY_ATTR_USAGE_CMDS] = { .len = NL802154_CMD_FRAME_NR_IDS / 8 },
+	[NL802154_KEY_ATTR_BYTES] = { .len = NL802154_KEY_SIZE },
+};
+
+static int nl802154_add_llsec_key(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
+	struct ieee802154_llsec_key key = { };
+	struct ieee802154_llsec_key_id id = { };
+	u32 commands[NL802154_CMD_FRAME_NR_IDS / 32] = { };
+
+	if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX,
+			     info->attrs[NL802154_ATTR_SEC_KEY],
+			     nl802154_key_policy))
+		return -EINVAL;
+
+	if (!attrs[NL802154_KEY_ATTR_USAGE_FRAMES] ||
+	    !attrs[NL802154_KEY_ATTR_BYTES])
+		return -EINVAL;
+
+	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
+		return -ENOBUFS;
+
+	key.frame_types = nla_get_u8(attrs[NL802154_KEY_ATTR_USAGE_FRAMES]);
+	if (key.frame_types > BIT(NL802154_FRAME_MAX) ||
+	    ((key.frame_types & BIT(NL802154_FRAME_CMD)) &&
+	     !attrs[NL802154_KEY_ATTR_USAGE_CMDS]))
+		return -EINVAL;
+
+	if (attrs[NL802154_KEY_ATTR_USAGE_CMDS]) {
+		/* TODO for each nested */
+		nla_memcpy(commands, attrs[NL802154_KEY_ATTR_USAGE_CMDS],
+			   NL802154_CMD_FRAME_NR_IDS / 8);
+
+		/* TODO understand the -EINVAL logic here? last condition */
+		if (commands[0] || commands[1] || commands[2] || commands[3] ||
+		    commands[4] || commands[5] || commands[6] ||
+		    commands[7] > BIT(NL802154_CMD_FRAME_MAX))
+			return -EINVAL;
+
+		key.cmd_frame_ids = commands[7];
+	} else {
+		key.cmd_frame_ids = 0;
+	}
+
+	nla_memcpy(key.key, attrs[NL802154_KEY_ATTR_BYTES], NL802154_KEY_SIZE);
+
+	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
+		return -ENOBUFS;
+
+	return rdev_add_llsec_key(rdev, wpan_dev, &id, &key);
+}
+
+static int nl802154_del_llsec_key(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct nlattr *attrs[NL802154_KEY_ATTR_MAX + 1];
+	struct ieee802154_llsec_key_id id;
+
+	if (nla_parse_nested(attrs, NL802154_KEY_ATTR_MAX,
+			     info->attrs[NL802154_ATTR_SEC_KEY],
+			     nl802154_key_policy))
+		return -EINVAL;
+
+	if (ieee802154_llsec_parse_key_id(attrs[NL802154_KEY_ATTR_ID], &id) < 0)
+		return -ENOBUFS;
+
+	return rdev_del_llsec_key(rdev, wpan_dev, &id);
+}
+
+static int nl802154_send_device(struct sk_buff *msg, u32 cmd, u32 portid,
+				u32 seq, int flags,
+				struct cfg802154_registered_device *rdev,
+				struct net_device *dev,
+				const struct ieee802154_llsec_device *dev_desc)
+{
+	void *hdr;
+	struct nlattr *nl_device;
+
+	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+	if (!hdr)
+		return -1;
+
+	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+		goto nla_put_failure;
+
+	nl_device = nla_nest_start(msg, NL802154_ATTR_SEC_DEVICE);
+	if (!nl_device)
+		goto nla_put_failure;
+
+	if (nla_put_u32(msg, NL802154_DEV_ATTR_FRAME_COUNTER,
+			dev_desc->frame_counter) ||
+	    nla_put_le16(msg, NL802154_DEV_ATTR_PAN_ID, dev_desc->pan_id) ||
+	    nla_put_le16(msg, NL802154_DEV_ATTR_SHORT_ADDR,
+			 dev_desc->short_addr) ||
+	    nla_put_le64(msg, NL802154_DEV_ATTR_EXTENDED_ADDR,
+			 dev_desc->hwaddr) ||
+	    nla_put_u8(msg, NL802154_DEV_ATTR_SECLEVEL_EXEMPT,
+		       dev_desc->seclevel_exempt) ||
+	    nla_put_u32(msg, NL802154_DEV_ATTR_KEY_MODE, dev_desc->key_mode))
+		goto nla_put_failure;
+
+	nla_nest_end(msg, nl_device);
+	genlmsg_end(msg, hdr);
+
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_dev(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct cfg802154_registered_device *rdev = NULL;
+	struct ieee802154_llsec_device *dev;
+	struct ieee802154_llsec_table *table;
+	struct wpan_dev *wpan_dev;
+	int err;
+
+	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+	if (err)
+		return err;
+
+	if (!wpan_dev->netdev) {
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	rdev_lock_llsec_table(rdev, wpan_dev);
+	rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+	/* TODO make it like station dump */
+	if (cb->args[2])
+		goto out;
+
+	list_for_each_entry(dev, &table->devices, list) {
+		if (nl802154_send_device(skb, NL802154_CMD_NEW_SEC_LEVEL,
+					 NETLINK_CB(cb->skb).portid,
+					 cb->nlh->nlmsg_seq, NLM_F_MULTI,
+					 rdev, wpan_dev->netdev, dev) < 0) {
+			/* TODO */
+			err = -EIO;
+			rdev_unlock_llsec_table(rdev, wpan_dev);
+			goto out_err;
+		}
+	}
+
+	cb->args[2] = 1;
+
+out:
+	rdev_unlock_llsec_table(rdev, wpan_dev);
+	err = skb->len;
+out_err:
+	nl802154_finish_wpan_dev_dump(rdev);
+
+	return err;
+}
+
+static const struct nla_policy nl802154_dev_policy[NL802154_DEV_ATTR_MAX + 1] = {
+	[NL802154_DEV_ATTR_FRAME_COUNTER] = { NLA_U32 },
+	[NL802154_DEV_ATTR_PAN_ID] = { .type = NLA_U16 },
+	[NL802154_DEV_ATTR_SHORT_ADDR] = { .type = NLA_U16 },
+	[NL802154_DEV_ATTR_EXTENDED_ADDR] = { .type = NLA_U64 },
+	[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] = { NLA_U8 },
+	[NL802154_DEV_ATTR_KEY_MODE] = { NLA_U32 },
+};
+
+static int
+ieee802154_llsec_parse_device(struct nlattr *nla,
+			      struct ieee802154_llsec_device *dev)
+{
+	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
+
+	if (!nla || nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX, nla,
+				     nl802154_dev_policy))
+		return -EINVAL;
+
+	memset(dev, 0, sizeof(*dev));
+
+	if (!attrs[NL802154_DEV_ATTR_FRAME_COUNTER] ||
+	    !attrs[NL802154_DEV_ATTR_PAN_ID] ||
+	    !attrs[NL802154_DEV_ATTR_SHORT_ADDR] ||
+	    !attrs[NL802154_DEV_ATTR_EXTENDED_ADDR] ||
+	    !attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT] ||
+	    !attrs[NL802154_DEV_ATTR_KEY_MODE])
+		return -EINVAL;
+
+	/* TODO be32 */
+	dev->frame_counter = nla_get_u32(attrs[NL802154_DEV_ATTR_FRAME_COUNTER]);
+	dev->pan_id = nla_get_le16(attrs[NL802154_DEV_ATTR_PAN_ID]);
+	dev->short_addr = nla_get_le16(attrs[NL802154_DEV_ATTR_SHORT_ADDR]);
+	/* TODO rename hwaddr to extended_addr */
+	dev->hwaddr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
+	dev->seclevel_exempt = nla_get_u8(attrs[NL802154_DEV_ATTR_SECLEVEL_EXEMPT]);
+	dev->key_mode = nla_get_u32(attrs[NL802154_DEV_ATTR_KEY_MODE]);
+
+	if (dev->key_mode > NL802154_DEVKEY_MAX ||
+	    (dev->seclevel_exempt != 0 && dev->seclevel_exempt != 1))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int nl802154_add_llsec_dev(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct ieee802154_llsec_device dev_desc;
+
+	if (ieee802154_llsec_parse_device(info->attrs[NL802154_ATTR_SEC_DEVICE],
+					  &dev_desc) < 0)
+		return -EINVAL;
+
+	return rdev_add_device(rdev, wpan_dev, &dev_desc);
+}
+
+static int nl802154_del_llsec_dev(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct nlattr *attrs[NL802154_DEV_ATTR_MAX + 1];
+	__le64 extended_addr;
+
+	if (nla_parse_nested(attrs, NL802154_DEV_ATTR_MAX,
+			     info->attrs[NL802154_ATTR_SEC_DEVICE],
+			     nl802154_dev_policy))
+		return -EINVAL;
+
+	if (!attrs[NL802154_DEV_ATTR_EXTENDED_ADDR])
+		return -EINVAL;
+
+	extended_addr = nla_get_le64(attrs[NL802154_DEV_ATTR_EXTENDED_ADDR]);
+	return rdev_del_device(rdev, wpan_dev, extended_addr);
+}
+
+static int nl802154_send_devkey(struct sk_buff *msg, u32 cmd, u32 portid,
+				u32 seq, int flags,
+				struct cfg802154_registered_device *rdev,
+				struct net_device *dev, __le64 extended_addr,
+				const struct ieee802154_llsec_device_key *devkey)
+{
+	void *hdr;
+	struct nlattr *nl_devkey, *nl_key_id;
+
+	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+	if (!hdr)
+		return -1;
+
+	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+		goto nla_put_failure;
+
+	nl_devkey = nla_nest_start(msg, NL802154_ATTR_SEC_DEVKEY);
+	if (!nl_devkey)
+		goto nla_put_failure;
+
+	if (nla_put_le64(msg, NL802154_DEVKEY_ATTR_EXTENDED_ADDR,
+			 extended_addr) ||
+	    nla_put_u32(msg, NL802154_DEVKEY_ATTR_FRAME_COUNTER,
+			devkey->frame_counter))
+		goto nla_put_failure;
+
+	nl_key_id = nla_nest_start(msg, NL802154_DEVKEY_ATTR_ID);
+	if (!nl_key_id)
+		goto nla_put_failure;
+
+	if (ieee802154_llsec_send_key_id(msg, &devkey->key_id) < 0)
+		goto nla_put_failure;
+
+	nla_nest_end(msg, nl_key_id);
+	nla_nest_end(msg, nl_devkey);
+	genlmsg_end(msg, hdr);
+
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_devkey(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct cfg802154_registered_device *rdev = NULL;
+	struct ieee802154_llsec_device_key *kpos;
+	struct ieee802154_llsec_device *dpos;
+	struct ieee802154_llsec_table *table;
+	struct wpan_dev *wpan_dev;
+	int err;
+
+	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+	if (err)
+		return err;
+
+	if (!wpan_dev->netdev) {
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	rdev_lock_llsec_table(rdev, wpan_dev);
+	rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+	/* TODO make it like station dump */
+	if (cb->args[2])
+		goto out;
+
+	/* TODO look if remove devkey and do some nested attribute */
+	list_for_each_entry(dpos, &table->devices, list) {
+		list_for_each_entry(kpos, &dpos->keys, list) {
+			if (nl802154_send_devkey(skb,
+						 NL802154_CMD_NEW_SEC_LEVEL,
+						 NETLINK_CB(cb->skb).portid,
+						 cb->nlh->nlmsg_seq,
+						 NLM_F_MULTI, rdev,
+						 wpan_dev->netdev,
+						 dpos->hwaddr,
+						 kpos) < 0) {
+				/* TODO */
+				err = -EIO;
+				rdev_unlock_llsec_table(rdev, wpan_dev);
+				goto out_err;
+			}
+		}
+	}
+
+	cb->args[2] = 1;
+
+out:
+	rdev_unlock_llsec_table(rdev, wpan_dev);
+	err = skb->len;
+out_err:
+	nl802154_finish_wpan_dev_dump(rdev);
+
+	return err;
+}
+
+static const struct nla_policy nl802154_devkey_policy[NL802154_DEVKEY_ATTR_MAX + 1] = {
+	[NL802154_DEVKEY_ATTR_FRAME_COUNTER] = { NLA_U32 },
+	[NL802154_DEVKEY_ATTR_EXTENDED_ADDR] = { NLA_U64 },
+	[NL802154_DEVKEY_ATTR_ID] = { NLA_NESTED },
+};
+
+static int nl802154_add_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
+	struct ieee802154_llsec_device_key key;
+	__le64 extended_addr;
+
+	if (!info->attrs[NL802154_ATTR_SEC_DEVKEY] ||
+	    nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX,
+			     info->attrs[NL802154_ATTR_SEC_DEVKEY],
+			     nl802154_devkey_policy) < 0)
+		return -EINVAL;
+
+	if (!attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER] ||
+	    !attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
+		return -EINVAL;
+
+	/* TODO change key.id ? */
+	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
+					  &key.key_id) < 0)
+		return -ENOBUFS;
+
+	/* TODO be32 */
+	key.frame_counter = nla_get_u32(attrs[NL802154_DEVKEY_ATTR_FRAME_COUNTER]);
+	/* TODO change naming hwaddr -> extended_addr
+	 * check unique identifier short+pan OR extended_addr
+	 */
+	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
+	return rdev_add_devkey(rdev, wpan_dev, extended_addr, &key);
+}
+
+static int nl802154_del_llsec_devkey(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct nlattr *attrs[NL802154_DEVKEY_ATTR_MAX + 1];
+	struct ieee802154_llsec_device_key key;
+	__le64 extended_addr;
+
+	if (nla_parse_nested(attrs, NL802154_DEVKEY_ATTR_MAX,
+			     info->attrs[NL802154_ATTR_SEC_DEVKEY],
+			     nl802154_devkey_policy))
+		return -EINVAL;
+
+	if (!attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR])
+		return -EINVAL;
+
+	/* TODO change key.id ? */
+	if (ieee802154_llsec_parse_key_id(attrs[NL802154_DEVKEY_ATTR_ID],
+					  &key.key_id) < 0)
+		return -ENOBUFS;
+
+	/* TODO change naming hwaddr -> extended_addr
+	 * check unique identifier short+pan OR extended_addr
+	 */
+	extended_addr = nla_get_le64(attrs[NL802154_DEVKEY_ATTR_EXTENDED_ADDR]);
+	return rdev_del_devkey(rdev, wpan_dev, extended_addr, &key);
+}
+
+static int nl802154_send_seclevel(struct sk_buff *msg, u32 cmd, u32 portid,
+				  u32 seq, int flags,
+				  struct cfg802154_registered_device *rdev,
+				  struct net_device *dev,
+				  const struct ieee802154_llsec_seclevel *sl)
+{
+	void *hdr;
+	struct nlattr *nl_seclevel;
+
+	hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
+	if (!hdr)
+		return -1;
+
+	if (nla_put_u32(msg, NL802154_ATTR_IFINDEX, dev->ifindex))
+		goto nla_put_failure;
+
+	nl_seclevel = nla_nest_start(msg, NL802154_ATTR_SEC_LEVEL);
+	if (!nl_seclevel)
+		goto nla_put_failure;
+
+	if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_FRAME, sl->frame_type) ||
+	    nla_put_u32(msg, NL802154_SECLEVEL_ATTR_LEVELS, sl->sec_levels) ||
+	    nla_put_u8(msg, NL802154_SECLEVEL_ATTR_DEV_OVERRIDE,
+		       sl->device_override))
+		goto nla_put_failure;
+
+	if (sl->frame_type == NL802154_FRAME_CMD) {
+		if (nla_put_u32(msg, NL802154_SECLEVEL_ATTR_CMD_FRAME,
+				sl->cmd_frame_id))
+			goto nla_put_failure;
+	}
+
+	nla_nest_end(msg, nl_seclevel);
+	genlmsg_end(msg, hdr);
+
+	return 0;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	return -EMSGSIZE;
+}
+
+static int
+nl802154_dump_llsec_seclevel(struct sk_buff *skb, struct netlink_callback *cb)
+{
+	struct cfg802154_registered_device *rdev = NULL;
+	struct ieee802154_llsec_seclevel *sl;
+	struct ieee802154_llsec_table *table;
+	struct wpan_dev *wpan_dev;
+	int err;
+
+	err = nl802154_prepare_wpan_dev_dump(skb, cb, &rdev, &wpan_dev);
+	if (err)
+		return err;
+
+	if (!wpan_dev->netdev) {
+		err = -EINVAL;
+		goto out_err;
+	}
+
+	rdev_lock_llsec_table(rdev, wpan_dev);
+	rdev_get_llsec_table(rdev, wpan_dev, &table);
+
+	/* TODO make it like station dump */
+	if (cb->args[2])
+		goto out;
+
+	list_for_each_entry(sl, &table->security_levels, list) {
+		if (nl802154_send_seclevel(skb, NL802154_CMD_NEW_SEC_LEVEL,
+					   NETLINK_CB(cb->skb).portid,
+					   cb->nlh->nlmsg_seq, NLM_F_MULTI,
+					   rdev, wpan_dev->netdev, sl) < 0) {
+			/* TODO */
+			err = -EIO;
+			rdev_unlock_llsec_table(rdev, wpan_dev);
+			goto out_err;
+		}
+	}
+
+	cb->args[2] = 1;
+
+out:
+	rdev_unlock_llsec_table(rdev, wpan_dev);
+	err = skb->len;
+out_err:
+	nl802154_finish_wpan_dev_dump(rdev);
+
+	return err;
+}
+
+static const struct nla_policy nl802154_seclevel_policy[NL802154_SECLEVEL_ATTR_MAX + 1] = {
+	[NL802154_SECLEVEL_ATTR_LEVELS] = { .type = NLA_U8 },
+	[NL802154_SECLEVEL_ATTR_FRAME] = { .type = NLA_U32 },
+	[NL802154_SECLEVEL_ATTR_CMD_FRAME] = { .type = NLA_U32 },
+	[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE] = { .type = NLA_U8 },
+};
+
+static int
+llsec_parse_seclevel(struct nlattr *nla, struct ieee802154_llsec_seclevel *sl)
+{
+	struct nlattr *attrs[NL802154_SECLEVEL_ATTR_MAX + 1];
+
+	if (!nla || nla_parse_nested(attrs, NL802154_SECLEVEL_ATTR_MAX, nla,
+				     nl802154_seclevel_policy))
+		return -EINVAL;
+
+	memset(sl, 0, sizeof(*sl));
+
+	if (!attrs[NL802154_SECLEVEL_ATTR_LEVELS] ||
+	    !attrs[NL802154_SECLEVEL_ATTR_FRAME] ||
+	    !attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE])
+		return -EINVAL;
+
+	sl->sec_levels = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_LEVELS]);
+	sl->frame_type = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_FRAME]);
+	sl->device_override = nla_get_u8(attrs[NL802154_SECLEVEL_ATTR_DEV_OVERRIDE]);
+	if (sl->frame_type > NL802154_FRAME_MAX ||
+	    (sl->device_override != 0 && sl->device_override != 1))
+		return -EINVAL;
+
+	if (sl->frame_type == NL802154_FRAME_CMD) {
+		if (!attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME])
+			return -EINVAL;
+
+		sl->cmd_frame_id = nla_get_u32(attrs[NL802154_SECLEVEL_ATTR_CMD_FRAME]);
+		if (sl->cmd_frame_id > NL802154_CMD_FRAME_MAX)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int nl802154_add_llsec_seclevel(struct sk_buff *skb,
+				       struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct ieee802154_llsec_seclevel sl;
+
+	if (llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
+				 &sl) < 0)
+		return -EINVAL;
+
+	return rdev_add_seclevel(rdev, wpan_dev, &sl);
+}
+
+static int nl802154_del_llsec_seclevel(struct sk_buff *skb,
+				       struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wpan_dev *wpan_dev = dev->ieee802154_ptr;
+	struct ieee802154_llsec_seclevel sl;
+
+	if (!info->attrs[NL802154_ATTR_SEC_LEVEL] ||
+	    llsec_parse_seclevel(info->attrs[NL802154_ATTR_SEC_LEVEL],
+				 &sl) < 0)
+		return -EINVAL;
+
+	return rdev_del_seclevel(rdev, wpan_dev, &sl);
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
+#define NL802154_FLAG_NEED_WPAN_PHY	0x01
+#define NL802154_FLAG_NEED_NETDEV	0x02
+#define NL802154_FLAG_NEED_RTNL		0x04
+#define NL802154_FLAG_CHECK_NETDEV_UP	0x08
+#define NL802154_FLAG_NEED_NETDEV_UP	(NL802154_FLAG_NEED_NETDEV |\
+					 NL802154_FLAG_CHECK_NETDEV_UP)
+#define NL802154_FLAG_NEED_WPAN_DEV	0x10
+#define NL802154_FLAG_NEED_WPAN_DEV_UP	(NL802154_FLAG_NEED_WPAN_DEV |\
+					 NL802154_FLAG_CHECK_NETDEV_UP)
+
+static int nl802154_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
+			     struct genl_info *info)
+{
+	struct cfg802154_registered_device *rdev;
+	struct wpan_dev *wpan_dev;
+	struct net_device *dev;
+	bool rtnl = ops->internal_flags & NL802154_FLAG_NEED_RTNL;
+
+	if (rtnl)
+		rtnl_lock();
+
+	if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_PHY) {
+		rdev = cfg802154_get_dev_from_info(genl_info_net(info), info);
+		if (IS_ERR(rdev)) {
+			if (rtnl)
+				rtnl_unlock();
+			return PTR_ERR(rdev);
+		}
+		info->user_ptr[0] = rdev;
+	} else if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV ||
+		   ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+		ASSERT_RTNL();
+		wpan_dev = __cfg802154_wpan_dev_from_attrs(genl_info_net(info),
+							   info->attrs);
+		if (IS_ERR(wpan_dev)) {
+			if (rtnl)
+				rtnl_unlock();
+			return PTR_ERR(wpan_dev);
+		}
+
+		dev = wpan_dev->netdev;
+		rdev = wpan_phy_to_rdev(wpan_dev->wpan_phy);
+
+		if (ops->internal_flags & NL802154_FLAG_NEED_NETDEV) {
+			if (!dev) {
+				if (rtnl)
+					rtnl_unlock();
+				return -EINVAL;
+			}
+
+			info->user_ptr[1] = dev;
+		} else {
+			info->user_ptr[1] = wpan_dev;
+		}
+
+		if (dev) {
+			if (ops->internal_flags & NL802154_FLAG_CHECK_NETDEV_UP &&
+			    !netif_running(dev)) {
+				if (rtnl)
+					rtnl_unlock();
+				return -ENETDOWN;
+			}
+
+			dev_hold(dev);
+		}
+
+		info->user_ptr[0] = rdev;
+	}
+
+	return 0;
+}
+
+static void nl802154_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
+			       struct genl_info *info)
+{
+	if (info->user_ptr[1]) {
+		if (ops->internal_flags & NL802154_FLAG_NEED_WPAN_DEV) {
+			struct wpan_dev *wpan_dev = info->user_ptr[1];
+
+			if (wpan_dev->netdev)
+				dev_put(wpan_dev->netdev);
+		} else {
+			dev_put(info->user_ptr[1]);
+		}
+	}
+
+	if (ops->internal_flags & NL802154_FLAG_NEED_RTNL)
+		rtnl_unlock();
+}
+
+static const struct genl_ops nl802154_ops[] = {
+	{
+		.cmd = NL802154_CMD_GET_WPAN_PHY,
+		.doit = nl802154_get_wpan_phy,
+		.dumpit = nl802154_dump_wpan_phy,
+		.done = nl802154_dump_wpan_phy_done,
+		.policy = nl802154_policy,
+		/* can be retrieved by unprivileged users */
+		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_GET_INTERFACE,
+		.doit = nl802154_get_interface,
+		.dumpit = nl802154_dump_interface,
+		.policy = nl802154_policy,
+		/* can be retrieved by unprivileged users */
+		.internal_flags = NL802154_FLAG_NEED_WPAN_DEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_NEW_INTERFACE,
+		.doit = nl802154_new_interface,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
 				  NL802154_FLAG_NEED_RTNL,
 				  NL802154_FLAG_NEED_RTNL,
 	},
 	},
 	{
 	{
@@ -1289,6 +2303,119 @@ static const struct genl_ops nl802154_ops[] = {
 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
 		.internal_flags = NL802154_FLAG_NEED_NETDEV |
 				  NL802154_FLAG_NEED_RTNL,
 				  NL802154_FLAG_NEED_RTNL,
 	},
 	},
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	{
+		.cmd = NL802154_CMD_SET_SEC_PARAMS,
+		.doit = nl802154_set_llsec_params,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_GET_SEC_KEY,
+		/* TODO .doit by matching key id? */
+		.dumpit = nl802154_dump_llsec_key,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_NEW_SEC_KEY,
+		.doit = nl802154_add_llsec_key,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_DEL_SEC_KEY,
+		.doit = nl802154_del_llsec_key,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	/* TODO unique identifier must short+pan OR extended_addr */
+	{
+		.cmd = NL802154_CMD_GET_SEC_DEV,
+		/* TODO .doit by matching extended_addr? */
+		.dumpit = nl802154_dump_llsec_dev,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_NEW_SEC_DEV,
+		.doit = nl802154_add_llsec_dev,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_DEL_SEC_DEV,
+		.doit = nl802154_del_llsec_dev,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	/* TODO remove complete devkey, put it as nested? */
+	{
+		.cmd = NL802154_CMD_GET_SEC_DEVKEY,
+		/* TODO doit by matching ??? */
+		.dumpit = nl802154_dump_llsec_devkey,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_NEW_SEC_DEVKEY,
+		.doit = nl802154_add_llsec_devkey,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_DEL_SEC_DEVKEY,
+		.doit = nl802154_del_llsec_devkey,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_GET_SEC_LEVEL,
+		/* TODO .doit by matching frame_type? */
+		.dumpit = nl802154_dump_llsec_seclevel,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_NEW_SEC_LEVEL,
+		.doit = nl802154_add_llsec_seclevel,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+	{
+		.cmd = NL802154_CMD_DEL_SEC_LEVEL,
+		/* TODO match frame_type only? */
+		.doit = nl802154_del_llsec_seclevel,
+		.policy = nl802154_policy,
+		.flags = GENL_ADMIN_PERM,
+		.internal_flags = NL802154_FLAG_NEED_NETDEV |
+				  NL802154_FLAG_NEED_RTNL,
+	},
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 };
 
 
 /* initialisation/exit functions */
 /* initialisation/exit functions */

+ 109 - 0
net/ieee802154/rdev-ops.h

@@ -208,4 +208,113 @@ rdev_set_ackreq_default(struct cfg802154_registered_device *rdev,
 	return ret;
 	return ret;
 }
 }
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+/* TODO this is already a nl802154, so move into ieee802154 */
+static inline void
+rdev_get_llsec_table(struct cfg802154_registered_device *rdev,
+		     struct wpan_dev *wpan_dev,
+		     struct ieee802154_llsec_table **table)
+{
+	rdev->ops->get_llsec_table(&rdev->wpan_phy, wpan_dev, table);
+}
+
+static inline void
+rdev_lock_llsec_table(struct cfg802154_registered_device *rdev,
+		      struct wpan_dev *wpan_dev)
+{
+	rdev->ops->lock_llsec_table(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline void
+rdev_unlock_llsec_table(struct cfg802154_registered_device *rdev,
+			struct wpan_dev *wpan_dev)
+{
+	rdev->ops->unlock_llsec_table(&rdev->wpan_phy, wpan_dev);
+}
+
+static inline int
+rdev_get_llsec_params(struct cfg802154_registered_device *rdev,
+		      struct wpan_dev *wpan_dev,
+		      struct ieee802154_llsec_params *params)
+{
+	return rdev->ops->get_llsec_params(&rdev->wpan_phy, wpan_dev, params);
+}
+
+static inline int
+rdev_set_llsec_params(struct cfg802154_registered_device *rdev,
+		      struct wpan_dev *wpan_dev,
+		      const struct ieee802154_llsec_params *params,
+		      u32 changed)
+{
+	return rdev->ops->set_llsec_params(&rdev->wpan_phy, wpan_dev, params,
+					   changed);
+}
+
+static inline int
+rdev_add_llsec_key(struct cfg802154_registered_device *rdev,
+		   struct wpan_dev *wpan_dev,
+		   const struct ieee802154_llsec_key_id *id,
+		   const struct ieee802154_llsec_key *key)
+{
+	return rdev->ops->add_llsec_key(&rdev->wpan_phy, wpan_dev, id, key);
+}
+
+static inline int
+rdev_del_llsec_key(struct cfg802154_registered_device *rdev,
+		   struct wpan_dev *wpan_dev,
+		   const struct ieee802154_llsec_key_id *id)
+{
+	return rdev->ops->del_llsec_key(&rdev->wpan_phy, wpan_dev, id);
+}
+
+static inline int
+rdev_add_seclevel(struct cfg802154_registered_device *rdev,
+		  struct wpan_dev *wpan_dev,
+		  const struct ieee802154_llsec_seclevel *sl)
+{
+	return rdev->ops->add_seclevel(&rdev->wpan_phy, wpan_dev, sl);
+}
+
+static inline int
+rdev_del_seclevel(struct cfg802154_registered_device *rdev,
+		  struct wpan_dev *wpan_dev,
+		  const struct ieee802154_llsec_seclevel *sl)
+{
+	return rdev->ops->del_seclevel(&rdev->wpan_phy, wpan_dev, sl);
+}
+
+static inline int
+rdev_add_device(struct cfg802154_registered_device *rdev,
+		struct wpan_dev *wpan_dev,
+		const struct ieee802154_llsec_device *dev_desc)
+{
+	return rdev->ops->add_device(&rdev->wpan_phy, wpan_dev, dev_desc);
+}
+
+static inline int
+rdev_del_device(struct cfg802154_registered_device *rdev,
+		struct wpan_dev *wpan_dev, __le64 extended_addr)
+{
+	return rdev->ops->del_device(&rdev->wpan_phy, wpan_dev, extended_addr);
+}
+
+static inline int
+rdev_add_devkey(struct cfg802154_registered_device *rdev,
+		struct wpan_dev *wpan_dev, __le64 extended_addr,
+		const struct ieee802154_llsec_device_key *devkey)
+{
+	return rdev->ops->add_devkey(&rdev->wpan_phy, wpan_dev, extended_addr,
+				     devkey);
+}
+
+static inline int
+rdev_del_devkey(struct cfg802154_registered_device *rdev,
+		struct wpan_dev *wpan_dev, __le64 extended_addr,
+		const struct ieee802154_llsec_device_key *devkey)
+{
+	return rdev->ops->del_devkey(&rdev->wpan_phy, wpan_dev, extended_addr,
+				     devkey);
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 #endif /* __CFG802154_RDEV_OPS */
 #endif /* __CFG802154_RDEV_OPS */

+ 4 - 4
net/ieee802154/socket.c

@@ -273,7 +273,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
 		goto out;
 		goto out;
 	}
 	}
 
 
-	mtu = dev->mtu;
+	mtu = IEEE802154_MTU;
 	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 
 
 	if (size > mtu) {
 	if (size > mtu) {
@@ -637,7 +637,7 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
 		err = -ENXIO;
 		err = -ENXIO;
 		goto out;
 		goto out;
 	}
 	}
-	mtu = dev->mtu;
+	mtu = IEEE802154_MTU;
 	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 	pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 
 
 	if (size > mtu) {
 	if (size > mtu) {
@@ -676,8 +676,8 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
 	cb->seclevel = ro->seclevel;
 	cb->seclevel = ro->seclevel;
 	cb->seclevel_override = ro->seclevel_override;
 	cb->seclevel_override = ro->seclevel_override;
 
 
-	err = dev_hard_header(skb, dev, ETH_P_IEEE802154, &dst_addr,
-			      ro->bound ? &ro->src_addr : NULL, size);
+	err = wpan_dev_hard_header(skb, dev, &dst_addr,
+				   ro->bound ? &ro->src_addr : NULL, size);
 	if (err < 0)
 	if (err < 0)
 		goto out_skb;
 		goto out_skb;
 
 

+ 205 - 0
net/mac802154/cfg.c

@@ -266,6 +266,195 @@ ieee802154_set_ackreq_default(struct wpan_phy *wpan_phy,
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+static void
+ieee802154_get_llsec_table(struct wpan_phy *wpan_phy,
+			   struct wpan_dev *wpan_dev,
+			   struct ieee802154_llsec_table **table)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+	*table = &sdata->sec.table;
+}
+
+static void
+ieee802154_lock_llsec_table(struct wpan_phy *wpan_phy,
+			    struct wpan_dev *wpan_dev)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+	mutex_lock(&sdata->sec_mtx);
+}
+
+static void
+ieee802154_unlock_llsec_table(struct wpan_phy *wpan_phy,
+			      struct wpan_dev *wpan_dev)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+
+	mutex_unlock(&sdata->sec_mtx);
+}
+
+static int
+ieee802154_set_llsec_params(struct wpan_phy *wpan_phy,
+			    struct wpan_dev *wpan_dev,
+			    const struct ieee802154_llsec_params *params,
+			    int changed)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_set_params(&sdata->sec, params, changed);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_get_llsec_params(struct wpan_phy *wpan_phy,
+			    struct wpan_dev *wpan_dev,
+			    struct ieee802154_llsec_params *params)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_get_params(&sdata->sec, params);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_add_llsec_key(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+			 const struct ieee802154_llsec_key_id *id,
+			 const struct ieee802154_llsec_key *key)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_key_add(&sdata->sec, id, key);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_del_llsec_key(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+			 const struct ieee802154_llsec_key_id *id)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_key_del(&sdata->sec, id);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_add_seclevel(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+			const struct ieee802154_llsec_seclevel *sl)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_seclevel_add(&sdata->sec, sl);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_del_seclevel(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+			const struct ieee802154_llsec_seclevel *sl)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_seclevel_del(&sdata->sec, sl);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_add_device(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+		      const struct ieee802154_llsec_device *dev_desc)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_dev_add(&sdata->sec, dev_desc);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_del_device(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+		      __le64 extended_addr)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_dev_del(&sdata->sec, extended_addr);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_add_devkey(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+		      __le64 extended_addr,
+		      const struct ieee802154_llsec_device_key *key)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_devkey_add(&sdata->sec, extended_addr, key);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+
+static int
+ieee802154_del_devkey(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
+		      __le64 extended_addr,
+		      const struct ieee802154_llsec_device_key *key)
+{
+	struct net_device *dev = wpan_dev->netdev;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	int res;
+
+	mutex_lock(&sdata->sec_mtx);
+	res = mac802154_llsec_devkey_del(&sdata->sec, extended_addr, key);
+	mutex_unlock(&sdata->sec_mtx);
+
+	return res;
+}
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
+
 const struct cfg802154_ops mac802154_config_ops = {
 const struct cfg802154_ops mac802154_config_ops = {
 	.add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
 	.add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
 	.del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
 	.del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
@@ -284,4 +473,20 @@ const struct cfg802154_ops mac802154_config_ops = {
 	.set_max_frame_retries = ieee802154_set_max_frame_retries,
 	.set_max_frame_retries = ieee802154_set_max_frame_retries,
 	.set_lbt_mode = ieee802154_set_lbt_mode,
 	.set_lbt_mode = ieee802154_set_lbt_mode,
 	.set_ackreq_default = ieee802154_set_ackreq_default,
 	.set_ackreq_default = ieee802154_set_ackreq_default,
+#ifdef CONFIG_IEEE802154_NL802154_EXPERIMENTAL
+	.get_llsec_table = ieee802154_get_llsec_table,
+	.lock_llsec_table = ieee802154_lock_llsec_table,
+	.unlock_llsec_table = ieee802154_unlock_llsec_table,
+	/* TODO above */
+	.set_llsec_params = ieee802154_set_llsec_params,
+	.get_llsec_params = ieee802154_get_llsec_params,
+	.add_llsec_key = ieee802154_add_llsec_key,
+	.del_llsec_key = ieee802154_del_llsec_key,
+	.add_seclevel = ieee802154_add_seclevel,
+	.del_seclevel = ieee802154_del_seclevel,
+	.add_device = ieee802154_add_device,
+	.del_device = ieee802154_del_device,
+	.add_devkey = ieee802154_add_devkey,
+	.del_devkey = ieee802154_del_devkey,
+#endif /* CONFIG_IEEE802154_NL802154_EXPERIMENTAL */
 };
 };

+ 102 - 16
net/mac802154/iface.c

@@ -367,12 +367,11 @@ static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
 	return 0;
 	return 0;
 }
 }
 
 
-static int mac802154_header_create(struct sk_buff *skb,
-				   struct net_device *dev,
-				   unsigned short type,
-				   const void *daddr,
-				   const void *saddr,
-				   unsigned len)
+static int ieee802154_header_create(struct sk_buff *skb,
+				    struct net_device *dev,
+				    const struct ieee802154_addr *daddr,
+				    const struct ieee802154_addr *saddr,
+				    unsigned len)
 {
 {
 	struct ieee802154_hdr hdr;
 	struct ieee802154_hdr hdr;
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
@@ -423,24 +422,89 @@ static int mac802154_header_create(struct sk_buff *skb,
 	return hlen;
 	return hlen;
 }
 }
 
 
+static const struct wpan_dev_header_ops ieee802154_header_ops = {
+	.create		= ieee802154_header_create,
+};
+
+/* This header create functionality assumes a 8 byte array for
+ * source and destination pointer at maximum. To adapt this for
+ * the 802.15.4 dataframe header we use extended address handling
+ * here only and intra pan connection. fc fields are mostly fallback
+ * handling. For provide dev_hard_header for dgram sockets.
+ */
+static int mac802154_header_create(struct sk_buff *skb,
+				   struct net_device *dev,
+				   unsigned short type,
+				   const void *daddr,
+				   const void *saddr,
+				   unsigned len)
+{
+	struct ieee802154_hdr hdr;
+	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
+	struct wpan_dev *wpan_dev = &sdata->wpan_dev;
+	struct ieee802154_mac_cb cb = { };
+	int hlen;
+
+	if (!daddr)
+		return -EINVAL;
+
+	memset(&hdr.fc, 0, sizeof(hdr.fc));
+	hdr.fc.type = IEEE802154_FC_TYPE_DATA;
+	hdr.fc.ack_request = wpan_dev->ackreq;
+	hdr.seq = atomic_inc_return(&dev->ieee802154_ptr->dsn) & 0xFF;
+
+	/* TODO currently a workaround to give zero cb block to set
+	 * security parameters defaults according MIB.
+	 */
+	if (mac802154_set_header_security(sdata, &hdr, &cb) < 0)
+		return -EINVAL;
+
+	hdr.dest.pan_id = wpan_dev->pan_id;
+	hdr.dest.mode = IEEE802154_ADDR_LONG;
+	ieee802154_be64_to_le64(&hdr.dest.extended_addr, daddr);
+
+	hdr.source.pan_id = hdr.dest.pan_id;
+	hdr.source.mode = IEEE802154_ADDR_LONG;
+
+	if (!saddr)
+		hdr.source.extended_addr = wpan_dev->extended_addr;
+	else
+		ieee802154_be64_to_le64(&hdr.source.extended_addr, saddr);
+
+	hlen = ieee802154_hdr_push(skb, &hdr);
+	if (hlen < 0)
+		return -EINVAL;
+
+	skb_reset_mac_header(skb);
+	skb->mac_len = hlen;
+
+	if (len > ieee802154_max_payload(&hdr))
+		return -EMSGSIZE;
+
+	return hlen;
+}
+
 static int
 static int
 mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
 mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
 {
 {
 	struct ieee802154_hdr hdr;
 	struct ieee802154_hdr hdr;
-	struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
 
 
 	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
 	if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
 		pr_debug("malformed packet\n");
 		pr_debug("malformed packet\n");
 		return 0;
 		return 0;
 	}
 	}
 
 
-	*addr = hdr.source;
-	return sizeof(*addr);
+	if (hdr.source.mode == IEEE802154_ADDR_LONG) {
+		ieee802154_le64_to_be64(haddr, &hdr.source.extended_addr);
+		return IEEE802154_EXTENDED_ADDR_LEN;
+	}
+
+	return 0;
 }
 }
 
 
-static struct header_ops mac802154_header_ops = {
-	.create		= mac802154_header_create,
-	.parse		= mac802154_header_parse,
+static const struct header_ops mac802154_header_ops = {
+	.create         = mac802154_header_create,
+	.parse          = mac802154_header_parse,
 };
 };
 
 
 static const struct net_device_ops mac802154_wpan_ops = {
 static const struct net_device_ops mac802154_wpan_ops = {
@@ -471,9 +535,29 @@ static void ieee802154_if_setup(struct net_device *dev)
 	dev->addr_len		= IEEE802154_EXTENDED_ADDR_LEN;
 	dev->addr_len		= IEEE802154_EXTENDED_ADDR_LEN;
 	memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
 	memset(dev->broadcast, 0xff, IEEE802154_EXTENDED_ADDR_LEN);
 
 
-	dev->hard_header_len	= MAC802154_FRAME_HARD_HEADER_LEN;
-	dev->needed_tailroom	= 2 + 16; /* FCS + MIC */
-	dev->mtu		= IEEE802154_MTU;
+	/* Let hard_header_len set to IEEE802154_MIN_HEADER_LEN. AF_PACKET
+	 * will not send frames without any payload, but ack frames
+	 * has no payload, so substract one that we can send a 3 bytes
+	 * frame. The xmit callback assumes at least a hard header where two
+	 * bytes fc and sequence field are set.
+	 */
+	dev->hard_header_len	= IEEE802154_MIN_HEADER_LEN - 1;
+	/* The auth_tag header is for security and places in private payload
+	 * room of mac frame which stucks between payload and FCS field.
+	 */
+	dev->needed_tailroom	= IEEE802154_MAX_AUTH_TAG_LEN +
+				  IEEE802154_FCS_LEN;
+	/* The mtu size is the payload without mac header in this case.
+	 * We have a dynamic length header with a minimum header length
+	 * which is hard_header_len. In this case we let mtu to the size
+	 * of maximum payload which is IEEE802154_MTU - IEEE802154_FCS_LEN -
+	 * hard_header_len. The FCS which is set by hardware or ndo_start_xmit
+	 * and the minimum mac header which can be evaluated inside driver
+	 * layer. The rest of mac header will be part of payload if greater
+	 * than hard_header_len.
+	 */
+	dev->mtu		= IEEE802154_MTU - IEEE802154_FCS_LEN -
+				  dev->hard_header_len;
 	dev->tx_queue_len	= 300;
 	dev->tx_queue_len	= 300;
 	dev->flags		= IFF_NOARP | IFF_BROADCAST;
 	dev->flags		= IFF_NOARP | IFF_BROADCAST;
 }
 }
@@ -513,6 +597,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
 		sdata->dev->netdev_ops = &mac802154_wpan_ops;
 		sdata->dev->netdev_ops = &mac802154_wpan_ops;
 		sdata->dev->ml_priv = &mac802154_mlme_wpan;
 		sdata->dev->ml_priv = &mac802154_mlme_wpan;
 		wpan_dev->promiscuous_mode = false;
 		wpan_dev->promiscuous_mode = false;
+		wpan_dev->header_ops = &ieee802154_header_ops;
 
 
 		mutex_init(&sdata->sec_mtx);
 		mutex_init(&sdata->sec_mtx);
 
 
@@ -550,7 +635,8 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
 	if (!ndev)
 	if (!ndev)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 
 
-	ndev->needed_headroom = local->hw.extra_tx_headroom;
+	ndev->needed_headroom = local->hw.extra_tx_headroom +
+				IEEE802154_MAX_HEADER_LEN;
 
 
 	ret = dev_alloc_name(ndev, ndev->name);
 	ret = dev_alloc_name(ndev, ndev->name);
 	if (ret < 0)
 	if (ret < 0)

+ 1 - 0
net/mac802154/llsec.c

@@ -401,6 +401,7 @@ int mac802154_llsec_dev_del(struct mac802154_llsec *sec, __le64 device_addr)
 
 
 	hash_del_rcu(&pos->bucket_s);
 	hash_del_rcu(&pos->bucket_s);
 	hash_del_rcu(&pos->bucket_hw);
 	hash_del_rcu(&pos->bucket_hw);
+	list_del_rcu(&pos->dev.list);
 	call_rcu(&pos->rcu, llsec_dev_free_rcu);
 	call_rcu(&pos->rcu, llsec_dev_free_rcu);
 
 
 	return 0;
 	return 0;

+ 4 - 0
net/mac802154/rx.c

@@ -87,6 +87,10 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
 
 
 	skb->dev = sdata->dev;
 	skb->dev = sdata->dev;
 
 
+	/* TODO this should be moved after netif_receive_skb call, otherwise
+	 * wireshark will show a mac header with security fields and the
+	 * payload is already decrypted.
+	 */
 	rc = mac802154_llsec_decrypt(&sdata->sec, skb);
 	rc = mac802154_llsec_decrypt(&sdata->sec, skb);
 	if (rc) {
 	if (rc) {
 		pr_debug("decryption failed: %i\n", rc);
 		pr_debug("decryption failed: %i\n", rc);

+ 4 - 3
net/mac802154/tx.c

@@ -77,9 +77,6 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
 		put_unaligned_le16(crc, skb_put(skb, 2));
 		put_unaligned_le16(crc, skb_put(skb, 2));
 	}
 	}
 
 
-	if (skb_cow_head(skb, local->hw.extra_tx_headroom))
-		goto err_tx;
-
 	/* Stop the netif queue on each sub_if_data object. */
 	/* Stop the netif queue on each sub_if_data object. */
 	ieee802154_stop_queue(&local->hw);
 	ieee802154_stop_queue(&local->hw);
 
 
@@ -121,6 +118,10 @@ ieee802154_subif_start_xmit(struct sk_buff *skb, struct net_device *dev)
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
 	struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
 	int rc;
 	int rc;
 
 
+	/* TODO we should move it to wpan_dev_hard_header and dev_hard_header
+	 * functions. The reason is wireshark will show a mac header which is
+	 * with security fields but the payload is not encrypted.
+	 */
 	rc = mac802154_llsec_encrypt(&sdata->sec, skb);
 	rc = mac802154_llsec_encrypt(&sdata->sec, skb);
 	if (rc) {
 	if (rc) {
 		netdev_warn(dev, "encryption failed: %i\n", rc);
 		netdev_warn(dev, "encryption failed: %i\n", rc);