]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
authorDavid S. Miller <davem@davemloft.net>
Sun, 11 Oct 2015 12:15:30 +0000 (05:15 -0700)
committerDavid S. Miller <davem@davemloft.net>
Sun, 11 Oct 2015 12:15:30 +0000 (05:15 -0700)
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>
60 files changed:
Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt [new file with mode: 0644]
MAINTAINERS
drivers/bluetooth/Kconfig
drivers/bluetooth/bfusb.c
drivers/bluetooth/bluecard_cs.c
drivers/bluetooth/bpa10x.c
drivers/bluetooth/bt3c_cs.c
drivers/bluetooth/btbcm.c
drivers/bluetooth/btintel.c
drivers/bluetooth/btintel.h
drivers/bluetooth/btmrvl_main.c
drivers/bluetooth/btmrvl_sdio.c
drivers/bluetooth/btsdio.c
drivers/bluetooth/btuart_cs.c
drivers/bluetooth/btusb.c
drivers/bluetooth/btwilink.c
drivers/bluetooth/dtl1_cs.c
drivers/bluetooth/hci_bcm.c
drivers/bluetooth/hci_h4.c
drivers/bluetooth/hci_h5.c
drivers/bluetooth/hci_intel.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_qca.c
drivers/bluetooth/hci_uart.h
drivers/bluetooth/hci_vhci.c
drivers/net/ieee802154/Kconfig
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/mrf24j40.c
include/linux/ieee802154.h
include/net/6lowpan.h
include/net/bluetooth/bluetooth.h
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/hci_mon.h
include/net/cfg802154.h
include/net/ieee802154_netdev.h
include/net/mac802154.h
include/net/netlink.h
include/net/nl802154.h
net/6lowpan/core.c
net/6lowpan/nhc.h
net/bluetooth/6lowpan.c
net/bluetooth/hci_core.c
net/bluetooth/hci_sock.c
net/bluetooth/lib.c
net/ieee802154/6lowpan/core.c
net/ieee802154/6lowpan/rx.c
net/ieee802154/6lowpan/tx.c
net/ieee802154/Kconfig
net/ieee802154/core.c
net/ieee802154/core.h
net/ieee802154/header_ops.c
net/ieee802154/nl802154.c
net/ieee802154/rdev-ops.h
net/ieee802154/socket.c
net/mac802154/cfg.c
net/mac802154/iface.c
net/mac802154/llsec.c
net/mac802154/rx.c
net/mac802154/tx.c

diff --git a/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt b/Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
new file mode 100644 (file)
index 0000000..a4ed2ef
--- /dev/null
@@ -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>;
+       };
index c22e13e80b6aca6ff53217d71f9e28750106a3e8..4d0171c4f6d81d0fef090ee42516502536738949 100644 (file)
@@ -6978,6 +6978,7 @@ M:        Alan Ott <alan@signal11.us>
 L:     linux-wpan@vger.kernel.org
 S:     Maintained
 F:     drivers/net/ieee802154/mrf24j40.c
+F:     Documentation/devicetree/bindings/net/ieee802154/mrf24j40.txt
 
 MSI LAPTOP SUPPORT
 M:     "Lee, Chun-Yi" <jlee@suse.com>
index 0bd88c942a5202845960ddbf62b35e9d1aa9e9d2..c9c5dd0bad364e2f4850a33fd95ee90e19a80a18 100644 (file)
@@ -4,6 +4,7 @@ menu "Bluetooth device drivers"
 
 config BT_INTEL
        tristate
+       select REGMAP
 
 config BT_BCM
        tristate
@@ -183,6 +184,7 @@ config BT_HCIBCM203X
 config BT_HCIBPA10X
        tristate "HCI BPA10x USB driver"
        depends on USB
+       select BT_HCIUART_H4
        help
          Bluetooth HCI BPA10x USB driver.
          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.
 
          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
          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.
 
          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.
 
          Say Y here to compile support for Marvell BT-over-SDIO driver
index a5c4d0584389713522652c472cfd5c7b5e75dbab..616ec2ac1b22f8d3e77f29848f3cd46cf830a94c 100644 (file)
@@ -422,17 +422,12 @@ static int bfusb_open(struct hci_dev *hdev)
 
        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);
 
        err = bfusb_rx_submit(data, NULL);
        if (!err) {
                for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
                        bfusb_rx_submit(data, NULL);
-       } else {
-               clear_bit(HCI_RUNNING, &hdev->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);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        write_lock_irqsave(&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);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
index 35e63aaa6f803e423a39e532adeff717b7279cb3..36fa1c958c74fadbb6bed47eca40a38e48433be0 100644 (file)
@@ -390,7 +390,7 @@ static void bluecard_receive(struct bluecard_info *info,
        for (i = 0; i < len; i++) {
 
                /* Allocate packet */
-               if (info->rx_skb == NULL) {
+               if (!info->rx_skb) {
                        info->rx_state = RECV_WAIT_PACKET_TYPE;
                        info->rx_count = 0;
                        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)))
                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))) {
                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);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        bluecard_hci_flush(hdev);
 
        if (test_bit(CARD_HAS_PCCARD_ID, &(info->hw_state))) {
index 8a319913c9a96fd60c6d021c5edd055b83ef0aad..49c397e21b39886ed409461a30479cfcc427772c 100644 (file)
@@ -35,7 +35,9 @@
 #include <net/bluetooth/bluetooth.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[] = {
        /* Tektronix BPA 100/105 (Digianswer) */
@@ -56,112 +58,6 @@ struct bpa10x_data {
        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)
 {
        struct sk_buff *skb = urb->context;
@@ -184,6 +80,22 @@ done:
        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)
 {
        struct hci_dev *hdev = urb->context;
@@ -197,11 +109,17 @@ static void bpa10x_rx_complete(struct urb *urb)
                return;
 
        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->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);
                        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);
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        err = bpa10x_submit_intr_urb(hdev);
        if (err < 0)
                goto error;
@@ -320,8 +235,6 @@ static int bpa10x_open(struct hci_dev *hdev)
 error:
        usb_kill_anchored_urbs(&data->rx_anchor);
 
-       clear_bit(HCI_RUNNING, &hdev->flags);
-
        return err;
 }
 
@@ -331,9 +244,6 @@ static int bpa10x_close(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        usb_kill_anchored_urbs(&data->rx_anchor);
 
        return 0;
@@ -350,6 +260,24 @@ static int bpa10x_flush(struct hci_dev *hdev)
        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)
 {
        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);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        skb->dev = (void *) hdev;
 
        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;
 }
 
+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)
 {
        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->close    = bpa10x_close;
        hdev->flush    = bpa10x_flush;
+       hdev->setup    = bpa10x_setup;
        hdev->send     = bpa10x_send_frame;
+       hdev->set_diag = bpa10x_set_diag;
 
        set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
 
index 772a2770710c7b7728dc529e4671d8f792922ca0..5803aaed958f9518bdf9b6683ae64aa24be0514e 100644 (file)
@@ -233,7 +233,7 @@ static void bt3c_receive(struct bt3c_info *info)
                info->hdev->stat.byte_rx++;
 
                /* Allocate packet */
-               if (info->rx_skb == NULL) {
+               if (!info->rx_skb) {
                        info->rx_state = RECV_WAIT_PACKET_TYPE;
                        info->rx_count = 0;
                        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 */
                                BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
                                info->hdev->stat.err_rx++;
-                               clear_bit(HCI_RUNNING, &(info->hdev->flags));
 
                                kfree_skb(info->rx_skb);
                                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)
 {
-       set_bit(HCI_RUNNING, &(hdev->flags));
-
        return 0;
 }
 
 
 static int bt3c_hci_close(struct hci_dev *hdev)
 {
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        bt3c_hci_flush(hdev);
 
        return 0;
index 02ed816a18f9a2b652bbd0d179d150f50cad0294..2fc363a0393dbe57e6c48de4a1770de8ac6c8b86 100644 (file)
@@ -181,6 +181,27 @@ static int btbcm_reset(struct hci_dev *hdev)
        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)
 {
        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]);
        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) {
        case 0:
        case 3:
@@ -464,6 +493,14 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
                hw_name ? : "BCM", (subver & 0x7000) >> 13,
                (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);
 
        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)
 {
        struct sk_buff *skb;
+       int err;
+
+       /* Reset */
+       err = btbcm_reset(hdev);
+       if (err)
+               return err;
 
        /* Read Verbose Config Version Info */
        skb = btbcm_read_verbose_config(hdev);
        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);
        }
 
index 9e18988375eb36b39ec9d9a762a381346aff5316..7047fe6a6a2b3b2f4ab7e952688ff084d9310009 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/module.h>
 #include <linux/firmware.h>
+#include <linux/regmap.h>
 
 #include <net/bluetooth/bluetooth.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);
 
+/* ------- 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_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
 MODULE_VERSION(VERSION);
index 52deaf2817cf1564da92e8dde268f3fa7c303ae4..f0655c476fd250a5e20dfc06cf45496aa4b433ae 100644 (file)
@@ -80,6 +80,9 @@ int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
                        const void *param);
 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
 
 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;
 }
 
+static inline struct regmap *btintel_regmap_init(struct hci_dev *hdev,
+                                                u16 opcode_read,
+                                                u16 opcode_write)
+{
+       return ERR_PTR(-EINVAL);
+}
 #endif
index bc110f61c8b18c661f58a83882df6c3ddb039221..6ba22862d788b952f204dbea85e15912d32bee1f 100644 (file)
@@ -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);
-       if (skb == NULL) {
+       if (!skb) {
                BT_ERR("No free skb");
                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);
 
-       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) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
@@ -477,9 +470,6 @@ static int btmrvl_close(struct hci_dev *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);
 
        return 0;
@@ -487,8 +477,6 @@ static int btmrvl_close(struct hci_dev *hdev)
 
 static int btmrvl_open(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &hdev->flags);
-
        return 0;
 }
 
index b9978a7ba0cc9b9883d2bdd95168f81408f6cc60..71ea2a3af29340b09ed1a44ccd3f22f840b8e25f 100644 (file)
@@ -146,6 +146,29 @@ static const struct btmrvl_sdio_card_reg btmrvl_reg_8897 = {
        .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 = {
        .helper         = "mrvl/sd8688_helper.bin",
        .firmware       = "mrvl/sd8688.bin",
@@ -191,25 +214,37 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = {
        .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[] = {
        /* Marvell SD8688 Bluetooth device */
        { 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 */
        { 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 */
        { 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 */
        { 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 */
        { SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL, 0x9136),
                        .driver_data = (unsigned long)&btmrvl_sdio_sd8887 },
        /* Marvell SD8897 Bluetooth device */
        { 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 */
 };
@@ -619,7 +654,7 @@ static int btmrvl_sdio_card_to_host(struct btmrvl_private *priv)
 
        /* Allocate buffer */
        skb = bt_skb_alloc(num_blocks * blksz + BTSDIO_DMA_ALIGN, GFP_ATOMIC);
-       if (skb == NULL) {
+       if (!skb) {
                BT_ERR("No free skb");
                ret = -ENOMEM;
                goto exit;
@@ -1278,6 +1313,12 @@ static void btmrvl_sdio_dump_firmware(struct btmrvl_private *priv)
 
                if (memory_size == 0) {
                        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;
                }
 
@@ -1616,3 +1657,4 @@ MODULE_FIRMWARE("mrvl/sd8787_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8797_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8887_uapsta.bin");
 MODULE_FIRMWARE("mrvl/sd8897_uapsta.bin");
+MODULE_FIRMWARE("mrvl/sd8997_uapsta.bin");
index 83f6437dd91df4014e27084f53ac6b02be5ed68a..7b624423a7e8f790e8448b421d39b1bf91616b23 100644 (file)
@@ -194,21 +194,15 @@ static int btsdio_open(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        sdio_claim_host(data->func);
 
        err = sdio_enable_func(data->func);
-       if (err < 0) {
-               clear_bit(HCI_RUNNING, &hdev->flags);
+       if (err < 0)
                goto release;
-       }
 
        err = sdio_claim_irq(data->func, btsdio_interrupt);
        if (err < 0) {
                sdio_disable_func(data->func);
-               clear_bit(HCI_RUNNING, &hdev->flags);
                goto release;
        }
 
@@ -229,9 +223,6 @@ static int btsdio_close(struct hci_dev *hdev)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        sdio_claim_host(data->func);
 
        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);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                hdev->stat.cmd_tx++;
index abb4d2106db464198c328af1069342bd36d64d80..bb8e4025fb9eb8d30d670ce03d9afcc245336e88 100644 (file)
@@ -38,7 +38,7 @@
 #include <linux/serial.h>
 #include <linux/serial_reg.h>
 #include <linux/bitops.h>
-#include <asm/io.h>
+#include <linux/io.h>
 
 #include <pcmcia/cistpl.h>
 #include <pcmcia/ciscode.h>
@@ -188,7 +188,7 @@ static void btuart_receive(struct btuart_info *info)
                info->hdev->stat.byte_rx++;
 
                /* Allocate packet */
-               if (info->rx_skb == NULL) {
+               if (!info->rx_skb) {
                        info->rx_state = RECV_WAIT_PACKET_TYPE;
                        info->rx_count = 0;
                        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 */
                                BT_ERR("Unknown HCI packet with type 0x%02x received", bt_cb(info->rx_skb)->pkt_type);
                                info->hdev->stat.err_rx++;
-                               clear_bit(HCI_RUNNING, &(info->hdev->flags));
 
                                kfree_skb(info->rx_skb);
                                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)
 {
-       set_bit(HCI_RUNNING, &(hdev->flags));
-
        return 0;
 }
 
 
 static int btuart_hci_close(struct hci_dev *hdev)
 {
-       if (!test_and_clear_bit(HCI_RUNNING, &(hdev->flags)))
-               return 0;
-
        btuart_hci_flush(hdev);
 
        return 0;
index dfaaea2efecb3fcf8806233ba40a6f03ff27a25c..247b1062cb9affc67d9e7f7a0234c167f90f4b79 100644 (file)
@@ -940,9 +940,6 @@ static int btusb_open(struct hci_dev *hdev)
 
        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))
                goto done;
 
@@ -965,7 +962,6 @@ done:
 
 failed:
        clear_bit(BTUSB_INTR_RUNNING, &data->flags);
-       clear_bit(HCI_RUNNING, &hdev->flags);
        usb_autopm_put_interface(data->intf);
        return err;
 }
@@ -984,9 +980,6 @@ static int btusb_close(struct hci_dev *hdev)
 
        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->waker);
 
@@ -1156,9 +1149,6 @@ static int btusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
 
        BT_DBG("%s", hdev->name);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                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);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        switch (bt_cb(skb)->pkt_type) {
        case HCI_COMMAND_PKT:
                if (test_bit(BTUSB_BOOTLOADER, &data->flags)) {
index 7a722df97343ee6f25fb4c02959b8d64bacc4449..57eb935aedc73b4a782d42f676ec06a0ad5bed5a 100644 (file)
@@ -155,9 +155,6 @@ static int ti_st_open(struct hci_dev *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 */
        hst = hci_get_drvdata(hdev);
 
@@ -181,7 +178,6 @@ static int ti_st_open(struct hci_dev *hdev)
                        goto done;
 
                if (err != -EINPROGRESS) {
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        BT_ERR("st_register failed %d", err);
                        return err;
                }
@@ -195,7 +191,6 @@ static int ti_st_open(struct hci_dev *hdev)
                        (&hst->wait_reg_completion,
                         msecs_to_jiffies(BT_REGISTER_TIMEOUT));
                if (!timeleft) {
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        BT_ERR("Timeout(%d sec),didn't get reg "
                                        "completion signal from ST",
                                        BT_REGISTER_TIMEOUT / 1000);
@@ -205,7 +200,6 @@ static int ti_st_open(struct hci_dev *hdev)
                /* Is ST registration callback
                 * called with ERROR status? */
                if (hst->reg_status != 0) {
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        BT_ERR("ST registration completed with invalid "
                                        "status %d", hst->reg_status);
                        return -EAGAIN;
@@ -215,7 +209,6 @@ done:
                hst->st_write = ti_st_proto[i].write;
                if (!hst->st_write) {
                        BT_ERR("undefined ST write function");
-                       clear_bit(HCI_RUNNING, &hdev->flags);
                        for (i = 0; i < MAX_BT_CHNL_IDS; i++) {
                                /* Undo registration with ST */
                                err = st_unregister(&ti_st_proto[i]);
@@ -236,9 +229,6 @@ static int ti_st_close(struct hci_dev *hdev)
        int err, i;
        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--) {
                err = st_unregister(&ti_st_proto[i]);
                if (err)
@@ -256,9 +246,6 @@ static int ti_st_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        struct ti_st *hst;
        long len;
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        hst = hci_get_drvdata(hdev);
 
        /* Prepend skb with frame type */
index 84135c54ed2e46df5111e2c2797b82fa8db4b143..5026f66fac88d2a7d8726a917ad0fe985be22a6a 100644 (file)
@@ -357,8 +357,6 @@ static irqreturn_t dtl1_interrupt(int irq, void *dev_inst)
 
 static int dtl1_hci_open(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &(hdev->flags));
-
        return 0;
 }
 
@@ -376,9 +374,6 @@ static int dtl1_hci_flush(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);
 
        return 0;
index f30654149c63b4099eb399b61aa5c5305907af10..645e66e9a94594d26dbb8ee929c88cca11ef447b 100644 (file)
@@ -32,6 +32,8 @@
 #include <linux/gpio/consumer.h>
 #include <linux/tty.h>
 #include <linux/interrupt.h>
+#include <linux/dmi.h>
+#include <linux/pm_runtime.h>
 
 #include <net/bluetooth/bluetooth.h>
 #include <net/bluetooth/hci_core.h>
 #include "btbcm.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 list_head        list;
 
@@ -55,7 +62,7 @@ struct bcm_device {
        int                     irq;
        u8                      irq_polarity;
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
        struct hci_uart         *hu;
        bool                    is_suspended; /* suspend/resume flag */
 #endif
@@ -152,13 +159,17 @@ static int bcm_gpio_set_power(struct bcm_device *dev, bool powered)
        return 0;
 }
 
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
 static irqreturn_t bcm_host_wake(int irq, void *data)
 {
        struct bcm_device *bdev = data;
 
        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;
 }
 
@@ -182,6 +193,12 @@ static int bcm_request_irq(struct bcm_data *bcm)
                        goto unlock;
 
                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:
@@ -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 */
        .host_wake_active = 0,  /* HOST_WAKE active mode: 1 = high, 0 = low */
        .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 */
        /* Irrelevant USB flags */
        .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; }
 #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)
 {
        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) {
                        bcm->dev = dev;
                        hu->init_speed = dev->init_speed;
-#ifdef CONFIG_PM_SLEEP
+#ifdef CONFIG_PM
                        dev->hu = hu;
 #endif
                        bcm_gpio_set_power(bcm->dev, true);
@@ -282,7 +322,10 @@ static int bcm_close(struct hci_uart *hu)
        mutex_lock(&bcm_device_lock);
        if (bcm_device_exists(bdev)) {
                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)) {
                        devm_free_irq(&bdev->pdev->dev, bdev->irq, bdev);
                        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);
 
+       hu->hdev->set_diag = bcm_set_diag;
        hu->hdev->set_bdaddr = btbcm_set_bdaddr;
 
        err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
@@ -379,10 +423,18 @@ finalize:
        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[] = {
-       { 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)
@@ -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);
                bcm->rx_skb = NULL;
                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;
@@ -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)
 {
        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));
-       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);
 
-               /* Once this callback returns, driver suspends BT via GPIO */
+               /* Once this returns, driver suspends BT via GPIO */
                bdev->is_suspended = true;
        }
 
@@ -452,6 +525,53 @@ static int bcm_suspend(struct device *dev)
                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)) {
                error = enable_irq_wake(bdev->irq);
                if (!error)
@@ -471,6 +591,10 @@ static int bcm_resume(struct device *dev)
 
        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);
 
        if (!bdev->hu)
@@ -481,22 +605,15 @@ static int bcm_resume(struct device *dev)
                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:
        mutex_unlock(&bcm_device_lock);
 
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
        return 0;
 }
 #endif
@@ -513,6 +630,22 @@ static const struct acpi_gpio_mapping acpi_bcm_default_gpios[] = {
 };
 
 #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)
 {
        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)
 {
        struct platform_device *pdev = dev->pdev;
-       const struct acpi_device_id *id;
-       struct acpi_device *adev;
        LIST_HEAD(resources);
+       const struct dmi_system_id *dmi_id;
        int ret;
 
-       id = acpi_match_device(pdev->dev.driver->acpi_match_table, &pdev->dev);
-       if (!id)
-               return -ENODEV;
-
        /* Retrieve GPIO data */
        dev->name = dev_name(&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 */
-       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;
 }
@@ -620,7 +755,6 @@ static int bcm_acpi_probe(struct bcm_device *dev)
 static int bcm_probe(struct platform_device *pdev)
 {
        struct bcm_device *dev;
-       struct acpi_device_id *pdata = pdev->dev.platform_data;
        int ret;
 
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
@@ -629,15 +763,9 @@ static int bcm_probe(struct platform_device *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);
 
@@ -693,7 +821,10 @@ MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
 #endif
 
 /* 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 = {
        .probe = bcm_probe,
index eec3f28e4bb9ffba89498a19e16e3a3320a164f2..a6fce48da0fb382df7e8600aea320e79ef05713c 100644 (file)
@@ -266,3 +266,4 @@ struct sk_buff *h4_recv_buf(struct hci_dev *hdev, struct sk_buff *skb,
 
        return skb;
 }
+EXPORT_SYMBOL_GPL(h4_recv_buf);
index b35b238a0380197fda6ee096e388a6fc09aa11a7..abee2216fdeb263bb6cc1d1052a070825f5926cd 100644 (file)
@@ -128,7 +128,7 @@ static void h5_timed_event(unsigned long arg)
 {
        const unsigned char sync_req[] = { 0x01, 0x7e };
        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 sk_buff *skb;
        unsigned long flags;
@@ -210,7 +210,7 @@ static int h5_open(struct hci_uart *hu)
 
        init_timer(&h5->timer);
        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;
 
@@ -453,7 +453,7 @@ static int h5_rx_pkt_start(struct hci_uart *hu, unsigned char c)
                return -ENOMEM;
        }
 
-       h5->rx_skb->dev = (void *) hu->hdev;
+       h5->rx_skb->dev = (void *)hu->hdev;
 
        return 0;
 }
@@ -696,7 +696,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
        }
 
        skb = skb_dequeue(&h5->unrel);
-       if (skb != NULL) {
+       if (skb) {
                nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
                                      skb->data, skb->len);
                if (nskb) {
@@ -714,7 +714,7 @@ static struct sk_buff *h5_dequeue(struct hci_uart *hu)
                goto unlock;
 
        skb = skb_dequeue(&h5->rel);
-       if (skb != NULL) {
+       if (skb) {
                nskb = h5_prepare_pkt(hu, bt_cb(skb)->pkt_type,
                                      skb->data, skb->len);
                if (nskb) {
index 49e25409de67b1e02be16d87154b8547579d6ca0..2952107e3baeb300634706ef72a8f1aeecb32220 100644 (file)
@@ -1165,22 +1165,6 @@ static const struct acpi_device_id 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
 
 #ifdef CONFIG_PM
@@ -1248,14 +1232,6 @@ static int intel_probe(struct platform_device *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",
                                              GPIOD_OUT_LOW);
        if (IS_ERR(idev->reset)) {
index 0d5a05a7c1fd1d46a8b77d71b94f97a9df4190b3..01a83a3f8a1d1d603a93a1fd9810b61120438b9c 100644 (file)
@@ -208,9 +208,6 @@ static int hci_uart_open(struct hci_dev *hdev)
        BT_DBG("%s %p", hdev->name, hdev);
 
        /* Nothing to do for UART driver */
-
-       set_bit(HCI_RUNNING, &hdev->flags);
-
        return 0;
 }
 
@@ -241,9 +238,6 @@ static int hci_uart_close(struct hci_dev *hdev)
 {
        BT_DBG("hdev %p", hdev);
 
-       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
-               return 0;
-
        hci_uart_flush(hdev);
        hdev->flush = NULL;
        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);
 
-       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);
 
        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->write_work, hci_uart_write_work);
 
-       spin_lock_init(&hu->rx_lock);
-
        /* Flush any pending characters in the driver and line discipline. */
 
        /* 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))
                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);
 
        if (hu->hdev)
                hu->hdev->stat.byte_rx += count;
 
-       spin_unlock(&hu->rx_lock);
-
        tty_unthrottle(tty);
 }
 
index 21f4ea4ce6103764c3282f9fda18dad8b1169709..b4a0393b2862f3a37eeb9893122c67362620346d 100644 (file)
@@ -347,7 +347,7 @@ static void hci_ibs_wake_retrans_timeout(unsigned long arg)
        struct hci_uart *hu = (struct hci_uart *)arg;
        struct qca_data *qca = hu->priv;
        unsigned long flags, retrans_delay;
-       unsigned long retransmit = 0;
+       bool retransmit = false;
 
        BT_DBG("hu %p wake retransmit timeout in %d 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) {
        case HCI_IBS_TX_WAKING:
                /* No WAKE_ACK, retransmit WAKE */
-               retransmit = 1;
+               retransmit = true;
                if (send_hci_ibs_cmd(HCI_IBS_WAKE_IND, hu) < 0) {
                        BT_ERR("Failed to acknowledge device wake up");
                        break;
index 495b9ef52bb0fd608519fda39c0b4726c95f3c7f..2f7bb35a890efc287d85e3fd9d46d022c19fc300 100644 (file)
@@ -85,7 +85,6 @@ struct hci_uart {
 
        struct sk_buff          *tx_skb;
        unsigned long           tx_state;
-       spinlock_t              rx_lock;
 
        unsigned int init_speed;
        unsigned int oper_speed;
index 78653db2ef2bca6d6ea085b8aa9054ce74d05e17..ed888e302bc396f000035a63ac310686262fc924 100644 (file)
@@ -55,8 +55,6 @@ struct vhci_data {
 
 static int vhci_open_dev(struct hci_dev *hdev)
 {
-       set_bit(HCI_RUNNING, &hdev->flags);
-
        return 0;
 }
 
@@ -64,9 +62,6 @@ static int vhci_close_dev(struct hci_dev *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);
 
        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);
 
-       if (!test_bit(HCI_RUNNING, &hdev->flags))
-               return -EBUSY;
-
        memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
        skb_queue_tail(&data->readq, skb);
 
index 5a614b2d0767588eb0680bfbc8e49117683b8f21..ce5f1a21e6d735b7f68436c104a86e2f7bf63dcb 100644 (file)
@@ -43,6 +43,7 @@ config IEEE802154_MRF24J40
        tristate "Microchip MRF24J40 transceiver driver"
        depends on IEEE802154_DRIVERS && MAC802154
        depends on SPI
+       select REGMAP_SPI
        ---help---
          Say Y here to enable the MRF24J20 SPI 802.15.4 wireless
          controller.
index 9756e6451038d671f571014d3fdc906a07363f62..de6e4fa2d6aafd24cd53ce7b8a7eb5f272c3d804 100644 (file)
@@ -81,7 +81,7 @@ struct at86rf230_state_change {
        u8 from_state;
        u8 to_state;
 
-       bool irq_enable;
+       bool free;
 };
 
 struct at86rf230_trac {
@@ -105,8 +105,6 @@ struct at86rf230_local {
        struct completion state_complete;
        struct at86rf230_state_change state;
 
-       struct at86rf230_state_change irq;
-
        unsigned long cal_timeout;
        bool is_tx;
        bool is_tx_from_off;
@@ -122,8 +120,7 @@ struct at86rf230_local {
 static void
 at86rf230_async_state_change(struct at86rf230_local *lp,
                             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
 at86rf230_sleep(struct at86rf230_local *lp)
@@ -352,8 +349,10 @@ at86rf230_async_error_recover(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        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);
+       if (ctx->free)
+               kfree(ctx);
 }
 
 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);
 
        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 */
 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,
-                        void (*complete)(void *context),
-                        const bool irq_enable)
+                        void (*complete)(void *context))
 {
        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;
        ctx->msg.complete = complete;
-       ctx->irq_enable = irq_enable;
        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);
-       }
 }
 
 static void
@@ -434,8 +442,7 @@ at86rf230_async_state_assert(void *context)
                                lp->tx_retry++;
 
                                at86rf230_async_state_change(lp, ctx, state,
-                                                            ctx->complete,
-                                                            ctx->irq_enable);
+                                                            ctx->complete);
                                return;
                        }
                }
@@ -456,8 +463,7 @@ static enum hrtimer_restart at86rf230_async_state_timer(struct hrtimer *timer)
        struct at86rf230_local *lp = ctx->lp;
 
        at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-                                at86rf230_async_state_assert,
-                                ctx->irq_enable);
+                                at86rf230_async_state_assert);
 
        return HRTIMER_NORESTART;
 }
@@ -562,14 +568,12 @@ at86rf230_async_state_change_start(void *context)
        struct at86rf230_local *lp = ctx->lp;
        u8 *buf = ctx->buf;
        const u8 trx_state = buf[1] & TRX_STATE_MASK;
-       int rc;
 
        /* Check for "possible" STATE_TRANSITION_IN_PROGRESS */
        if (trx_state == STATE_TRANSITION_IN_PROGRESS) {
                udelay(1);
                at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-                                        at86rf230_async_state_change_start,
-                                        ctx->irq_enable);
+                                        at86rf230_async_state_change_start);
                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
         * 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
 at86rf230_async_state_change(struct at86rf230_local *lp,
                             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 */
        ctx->to_state = state;
        ctx->complete = complete;
-       ctx->irq_enable = irq_enable;
        at86rf230_async_read_reg(lp, RG_TRX_STATUS, ctx,
-                                at86rf230_async_state_change_start,
-                                irq_enable);
+                                at86rf230_async_state_change_start);
 }
 
 static void
@@ -632,8 +625,7 @@ at86rf230_sync_state_change(struct at86rf230_local *lp, unsigned int state)
        unsigned long rc;
 
        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,
                                         msecs_to_jiffies(100));
@@ -651,9 +643,8 @@ at86rf230_tx_complete(void *context)
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
 
-       enable_irq(ctx->irq);
-
        ieee802154_xmit_complete(lp->hw, lp->tx_skb, false);
+       kfree(ctx);
 }
 
 static void
@@ -663,7 +654,7 @@ at86rf230_tx_on(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
-                                    at86rf230_tx_complete, true);
+                                    at86rf230_tx_complete);
 }
 
 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
@@ -706,7 +696,6 @@ at86rf230_rx_read_frame_complete(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
-       u8 rx_local_buf[AT86RF2XX_MAX_BUF];
        const u8 *buf = ctx->buf;
        struct sk_buff *skb;
        u8 len, lqi;
@@ -718,18 +707,16 @@ at86rf230_rx_read_frame_complete(void *context)
        }
        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);
        if (!skb) {
                dev_vdbg(&lp->spi->dev, "failed to allocate sk_buff\n");
+               kfree(ctx);
                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);
+       kfree(ctx);
 }
 
 static void
@@ -765,21 +752,23 @@ at86rf230_rx_trac_check(void *context)
        rc = spi_async(lp->spi, &ctx->msg);
        if (rc) {
                ctx->trx.len = 2;
-               enable_irq(ctx->irq);
                at86rf230_async_error(lp, ctx, rc);
        }
 }
 
 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) {
                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 {
-               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_local *lp = ctx->lp;
        const u8 *buf = ctx->buf;
-       const u8 irq = buf[1];
+       u8 irq = buf[1];
+
+       enable_irq(lp->spi->irq);
 
        if (irq & IRQ_TRX_END) {
-               at86rf230_irq_trx_end(lp);
+               at86rf230_irq_trx_end(ctx);
        } else {
-               enable_irq(ctx->irq);
                dev_err(&lp->spi->dev, "not supported irq %02x received\n",
                        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)
 {
        struct at86rf230_local *lp = data;
-       struct at86rf230_state_change *ctx = &lp->irq;
-       u8 *buf = ctx->buf;
+       struct at86rf230_state_change *ctx;
        int rc;
 
        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;
        rc = spi_async(lp->spi, &ctx->msg);
        if (rc) {
-               enable_irq(irq);
                at86rf230_async_error(lp, ctx, rc);
+               enable_irq(irq);
                return IRQ_NONE;
        }
 
@@ -826,21 +842,14 @@ at86rf230_write_frame_complete(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
-       u8 *buf = ctx->buf;
-       int rc;
 
        ctx->trx.len = 2;
 
-       if (gpio_is_valid(lp->slp_tr)) {
+       if (gpio_is_valid(lp->slp_tr))
                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
@@ -873,7 +882,7 @@ at86rf230_xmit_tx_on(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
-                                    at86rf230_write_frame, false);
+                                    at86rf230_write_frame);
 }
 
 static void
@@ -886,12 +895,10 @@ at86rf230_xmit_start(void *context)
        if (lp->is_tx_from_off) {
                lp->is_tx_from_off = false;
                at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
-                                            at86rf230_write_frame,
-                                            false);
+                                            at86rf230_write_frame);
        } else {
                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)) {
                lp->is_tx_from_off = true;
                at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
-                                            at86rf230_xmit_start, false);
+                                            at86rf230_xmit_start);
        } else {
                at86rf230_xmit_start(ctx);
        }
@@ -1373,10 +1380,6 @@ static int at86rf230_hw_init(struct at86rf230_local *lp, u8 xtal_trim)
                return rc;
 
        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 ||
            irq_type == IRQ_TYPE_LEVEL_LOW)
                irq_pol = IRQ_ACTIVE_LOW;
@@ -1602,43 +1605,6 @@ not_supp:
        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
 static struct dentry *at86rf230_debugfs_root;
 
@@ -1760,7 +1726,8 @@ static int at86rf230_probe(struct spi_device *spi)
                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);
        if (rc < 0)
index 997724b8e4343b3ab068a86463393e8008f6ee91..aca0fb3cccbf5f294e689daae6722638356badab 100644 (file)
 #include <linux/spi/spi.h>
 #include <linux/interrupt.h>
 #include <linux/module.h>
+#include <linux/regmap.h>
 #include <linux/ieee802154.h>
+#include <linux/irq.h>
 #include <net/cfg802154.h>
 #include <net/mac802154.h>
 
 /* 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 */
-#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. */
 #define MRF24J40_CHAN_MIN 11
@@ -81,11 +202,52 @@ struct mrf24j40 {
        struct spi_device *spi;
        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. */
 #define MRF24J40_READSHORT(reg) ((reg) << 1)
 #define MRF24J40_WRITESHORT(reg) ((reg) << 1 | 1)
@@ -97,118 +259,304 @@ struct mrf24j40 {
 
 #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;
-       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)
-               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
@@ -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,
                        const u8 *data, size_t length)
 {
-       int ret;
        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.*/
        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;
        }
 
-       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);
-       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)
                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;
 }
 
 static int mrf24j40_tx(struct ieee802154_hw *hw, struct sk_buff *skb)
 {
        struct mrf24j40 *devrec = hw->priv;
-       u8 val;
-       int ret = 0;
 
        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)
@@ -394,33 +610,23 @@ static int mrf24j40_ed(struct ieee802154_hw *hw, u8 *level)
 static int mrf24j40_start(struct ieee802154_hw *hw)
 {
        struct mrf24j40 *devrec = hw->priv;
-       u8 val;
-       int ret;
 
        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)
 {
        struct mrf24j40 *devrec = hw->priv;
-       u8 val;
-       int ret;
 
        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)
@@ -436,21 +642,23 @@ static int mrf24j40_set_channel(struct ieee802154_hw *hw, u8 page, u8 channel)
        WARN_ON(channel > MRF24J40_CHAN_MAX);
 
        /* 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 */
-       ret = read_short_reg(devrec, REG_RFCTL, &val);
+       ret = regmap_update_bits(devrec->regmap_short, REG_RFCTL, BIT_RFRST,
+                                BIT_RFRST);
        if (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,
@@ -468,8 +676,8 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
                addrh = le16_to_cpu(filt->short_addr) >> 8 & 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),
                        "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);
                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
                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;
                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);
        }
@@ -507,14 +716,14 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
                u8 val;
                int ret;
 
-               ret = read_short_reg(devrec, REG_RXMCR, &val);
-               if (ret)
-                       return ret;
                if (filt->pan_coord)
-                       val |= 0x8;
+                       val = BIT_PANCOORD;
                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_ORDER is maintained as default (no beacon/superframe).
@@ -527,168 +736,392 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
        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)
-               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) {
-               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;
 }
 
 static const struct ieee802154_ops mrf24j40_ops = {
        .owner = THIS_MODULE,
-       .xmit_sync = mrf24j40_tx,
+       .xmit_async = mrf24j40_tx,
        .ed = mrf24j40_ed,
        .start = mrf24j40_start,
        .stop = mrf24j40_stop,
        .set_channel = mrf24j40_set_channel,
        .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 */
-       if (intstat & 0x1)
-               complete(&devrec->tx_complete);
+       if (intstat & BIT_TXNIF)
+               ieee802154_xmit_complete(devrec->hw, devrec->tx_skb, false);
 
        /* Check for Rx */
-       if (intstat & 0x8)
+       if (intstat & BIT_RXIF)
                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;
 }
 
 static int mrf24j40_hw_init(struct mrf24j40 *devrec)
 {
+       u32 irq_type;
        int ret;
-       u8 val;
 
        /* Initialize the device.
                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)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_PACON2, 0x98);
+       ret = regmap_write(devrec->regmap_short, REG_PACON2, 0x98);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_TXSTBL, 0x95);
+       ret = regmap_write(devrec->regmap_short, REG_TXSTBL, 0x95);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON0, 0x03);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON0, 0x03);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON1, 0x01);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON1, 0x01);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON2, 0x80);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON2, 0x80);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON6, 0x90);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON6, 0x90);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON7, 0x80);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON7, 0x80);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_RFCON8, 0x10);
+       ret = regmap_write(devrec->regmap_long, REG_RFCON8, 0x10);
        if (ret)
                goto err_ret;
 
-       ret = write_long_reg(devrec, REG_SLPCON1, 0x21);
+       ret = regmap_write(devrec->regmap_long, REG_SLPCON1, 0x21);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_BBREG2, 0x80);
+       ret = regmap_write(devrec->regmap_short, REG_BBREG2, 0x80);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_CCAEDTH, 0x60);
+       ret = regmap_write(devrec->regmap_short, REG_CCAEDTH, 0x60);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_BBREG6, 0x40);
+       ret = regmap_write(devrec->regmap_short, REG_BBREG6, 0x40);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_RFCTL, 0x04);
+       ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x04);
        if (ret)
                goto err_ret;
 
-       ret = write_short_reg(devrec, REG_RFCTL, 0x0);
+       ret = regmap_write(devrec->regmap_short, REG_RFCTL, 0x0);
        if (ret)
                goto err_ret;
 
        udelay(192);
 
        /* 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)
                goto err_ret;
 
@@ -696,22 +1129,39 @@ static int mrf24j40_hw_init(struct mrf24j40 *devrec)
                /* Enable external amplifier.
                 * 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.
                 * 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;
@@ -720,67 +1170,178 @@ err_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 */
 
-       devrec->hw = ieee802154_alloc_hw(0, &mrf24j40_ops);
-       if (!devrec->hw)
+       hw = ieee802154_alloc_hw(sizeof(*devrec), &mrf24j40_ops);
+       if (!hw)
                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->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;
+       }
+
+       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);
        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) {
                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;
 
-err_irq:
-err_hw_init:
-       ieee802154_unregister_hw(devrec->hw);
 err_register_device:
        ieee802154_free_hw(devrec->hw);
 err_ret:
@@ -801,6 +1362,14 @@ static int mrf24j40_remove(struct spi_device *spi)
        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[] = {
        { "mrf24j40", MRF24J40 },
        { "mrf24j40ma", MRF24J40MA },
@@ -811,6 +1380,7 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
 
 static struct spi_driver mrf24j40_driver = {
        .driver = {
+               .of_match_table = of_match_ptr(mrf24j40_of_match),
                .name = "mrf24j40",
                .owner = THIS_MODULE,
        },
index db01492814d31340b8825e231da6a130a3ebf10d..d3e415674dac4dfa34145c3f1d3a49685c70413c 100644 (file)
 
 #include <linux/types.h>
 #include <linux/random.h>
-#include <asm/byteorder.h>
 
 #define IEEE802154_MTU                 127
 #define IEEE802154_ACK_PSDU_LEN                5
 #define IEEE802154_MIN_PSDU_LEN                9
 #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_ADDR_SHORT_BROADCAST        0xffff
@@ -207,6 +217,7 @@ enum {
 
 /* frame control handling */
 #define IEEE802154_FCTL_FTYPE          0x0003
+#define IEEE802154_FCTL_ACKREQ         0x0020
 #define IEEE802154_FCTL_INTRA_PAN      0x0040
 
 #define IEEE802154_FTYPE_DATA          0x0001
@@ -221,6 +232,15 @@ static inline int ieee802154_is_data(__le16 fc)
                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
  * @fc: frame control bytes in little-endian byteorder
index eeae5eb58754c10869d55842271bfcb4bfa47db5..07db532696df2d5c71ff5993ce3f4c03a2818940 100644 (file)
 #define UIP_PROTO_UDP                  17 /* ipv6 next header value for UDP */
 #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
  * second bit-flip (Universe/Local) is done according RFC2464
index f5ade857339390d46ad45de1425a9c06d54b8128..c4defef319d596505190b33e2c6907a38990a5f0 100644 (file)
@@ -122,11 +122,14 @@ struct bt_voice {
 __printf(1, 2)
 void bt_info(const char *fmt, ...);
 __printf(1, 2)
+void bt_warn(const char *fmt, ...);
+__printf(1, 2)
 void bt_err(const char *fmt, ...);
 __printf(1, 2)
 void bt_err_ratelimited(const char *fmt, ...);
 
 #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_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, ...)                            \
        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, ...)                             \
        BT_ERR("%s: " fmt, (hdev)->name, ##__VA_ARGS__)
 #define bt_dev_dbg(hdev, fmt, ...)                             \
index 7ca6690355ea08acd54aa87b3302f78702c206c7..a26ff28ca878930f663db6b681dadbe138cf97b0 100644 (file)
@@ -44,6 +44,8 @@
 #define HCI_DEV_DOWN                   4
 #define HCI_DEV_SUSPEND                        5
 #define HCI_DEV_RESUME                 6
+#define HCI_DEV_OPEN                   7
+#define HCI_DEV_CLOSE                  8
 
 /* HCI notify events */
 #define HCI_NOTIFY_CONN_ADD            1
@@ -238,6 +240,7 @@ enum {
        HCI_LE_SCAN_INTERRUPTED,
 
        HCI_DUT_MODE,
+       HCI_VENDOR_DIAG,
        HCI_FORCE_BREDR_SMP,
        HCI_FORCE_STATIC_ADDR,
 
@@ -260,6 +263,7 @@ enum {
 #define HCI_ACLDATA_PKT                0x02
 #define HCI_SCODATA_PKT                0x03
 #define HCI_EVENT_PKT          0x04
+#define HCI_DIAG_PKT           0xf0
 #define HCI_VENDOR_PKT         0xff
 
 /* HCI packet types */
index 256e6734c1fbf93f025a3ebbb1ced9fae4002550..f28470e5968202bd2ad7646bbefa7d9ebd646f88 100644 (file)
@@ -398,6 +398,7 @@ struct hci_dev {
        int (*send)(struct hci_dev *hdev, struct sk_buff *skb);
        void (*notify)(struct hci_dev *hdev, unsigned int evt);
        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);
 };
 
@@ -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);
 
 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_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);
 
+struct sk_buff *hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
+                            const void *param, u32 timeout);
+
 /* ----- HCI Sockets ----- */
 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,
index 77d1e5764185acc0aea30c3cc4fc7eb2a3d79509..2b67567cf28d6d3c9b762ab0226a7818464428d2 100644 (file)
@@ -39,6 +39,10 @@ struct hci_mon_hdr {
 #define HCI_MON_ACL_RX_PKT     5
 #define HCI_MON_SCO_TX_PKT     6
 #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 {
        __u8            type;
@@ -48,4 +52,10 @@ struct hci_mon_new_index {
 } __packed;
 #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 */
index 76b1ffaea863600be09280cc4c2d168dc5bae41f..171cd76558fb8245fc267cbdddc7e9d675f28e39 100644 (file)
 struct wpan_phy;
 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 net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
                                                           const char *name,
@@ -65,6 +75,51 @@ struct cfg802154_ops {
                                struct wpan_dev *wpan_dev, bool mode);
        int     (*set_ackreq_default)(struct wpan_phy *wpan_phy,
                                      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
@@ -167,6 +222,102 @@ struct wpan_phy {
        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_phy *wpan_phy;
        int iftype;
@@ -175,6 +326,8 @@ struct wpan_dev {
        struct list_head list;
        struct net_device *netdev;
 
+       const struct wpan_dev_header_ops *header_ops;
+
        /* lowpan interface, set when the wpan_dev belongs to one 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)
 
+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 *
 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)
index 2c10a9f0c6d96ba51182abb60507d376ae543e96..a62a051a3a2facd78f57c60e120b9ea06bca9b11 100644 (file)
@@ -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 {
 #if defined(__LITTLE_ENDIAN_BITFIELD)
        u16 type:3,
@@ -99,7 +90,7 @@ struct ieee802154_hdr {
  * hdr->fc will be ignored. this includes the INTRA_PAN bit and the frame
  * 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
  * 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);
 }
 
-#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 {
        IEEE802154_LLSEC_DEVKEY_IGNORE,
        IEEE802154_LLSEC_DEVKEY_RESTRICT,
@@ -283,49 +242,6 @@ enum {
        __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_ACTIVE     1
 #define IEEE802154_MAC_SCAN_PASSIVE    2
index 32bd7c0467d4846ecbeb21f31e58f0ddb3d0b6a5..5718765cbd95ff7d8d608a4bda3639b0b6e1c7df 100644 (file)
 
 #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
  *
@@ -256,7 +248,7 @@ struct ieee802154_ops {
 static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb)
 {
        /* return some invalid fc on failure */
-       if (unlikely(skb->mac_len < 2)) {
+       if (unlikely(skb->len < 2)) {
                WARN_ON(1);
                return cpu_to_le16(0);
        }
index 2a5dbcc90d1cafc950a4eafd3fd18b4d1e342d47..0e31727517556dabdc929c496e9a5e6c995de068 100644 (file)
@@ -1003,6 +1003,15 @@ static inline __be32 nla_get_be32(const struct nlattr *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: u16 netlink attribute
@@ -1065,6 +1074,15 @@ static inline __be64 nla_get_be64(const struct nlattr *nla)
        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: s32 netlink attribute
index cf2713d8b975f11c6f7ba6725af165a53b5e82bb..32cb3e591e07b6a936a14788952c737c9bb277bd 100644 (file)
@@ -56,6 +56,22 @@ enum nl802154_commands {
 
        /* 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 */
        __NL802154_CMD_AFTER_LAST,
        NL802154_CMD_MAX = __NL802154_CMD_AFTER_LAST - 1
@@ -110,6 +126,18 @@ enum nl802154_attrs {
 
        /* 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_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
 };
 
+#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 */
index ae1896fa45e2f891a89fd76cb8c4da5a1031f5e7..83b19e072224b031403d5904f1d9a8de7eb05ee2 100644 (file)
 
 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;
 }
 EXPORT_SYMBOL(lowpan_netdev_setup);
index ed44938eb5dec6806ae18ad9cf316253eb37b2c3..c249f17fa37b8d0c5654c5abde57b38ba977a453 100644 (file)
@@ -8,8 +8,6 @@
 #include <net/6lowpan.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
  *
index 131e79cde3504f161e38f7bd5797f2de06eb63d1..db73b8a1433f86bfadd60aa9daae1b59d42e6192 100644 (file)
@@ -35,7 +35,6 @@ static struct dentry *lowpan_enable_debugfs;
 static struct dentry *lowpan_control_debugfs;
 
 #define IFACE_NAME_TEMPLATE "bt%d"
-#define EUI64_ADDR_LEN 8
 
 struct skb_cb {
        struct in6_addr addr;
@@ -674,13 +673,8 @@ static struct header_ops header_ops = {
 
 static void netdev_setup(struct net_device *dev)
 {
-       dev->addr_len           = EUI64_ADDR_LEN;
-       dev->type               = ARPHRD_6LOWPAN;
-
        dev->hard_header_len    = 0;
        dev->needed_tailroom    = 0;
-       dev->mtu                = IPV6_MIN_MTU;
-       dev->tx_queue_len       = 0;
        dev->flags              = IFF_RUNNING | IFF_POINTOPOINT |
                                  IFF_MULTICAST;
        dev->watchdog_timeo     = 0;
@@ -775,24 +769,7 @@ static struct l2cap_chan *chan_create(void)
 
        chan->chan_type = L2CAP_CHAN_CONN_ORIENTED;
        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;
 }
@@ -919,7 +896,10 @@ static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *pchan)
 {
        struct l2cap_chan *chan;
 
-       chan = chan_open(pchan);
+       chan = chan_create();
+       if (!chan)
+               return NULL;
+
        chan->ops = pchan->ops;
 
        BT_DBG("chan %p pchan %p", chan, pchan);
@@ -1065,34 +1045,23 @@ static inline __u8 bdaddr_type(__u8 type)
                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)
 {
-       struct l2cap_chan *pchan;
+       struct l2cap_chan *chan;
        int err;
 
-       pchan = chan_get();
-       if (!pchan)
+       chan = chan_create();
+       if (!chan)
                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);
 
-       BT_DBG("chan %p err %d", pchan, err);
+       BT_DBG("chan %p err %d", chan, err);
        if (err < 0)
-               l2cap_chan_put(pchan);
+               l2cap_chan_put(chan);
 
        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)
 {
        bdaddr_t *addr = BDADDR_ANY;
-       struct l2cap_chan *pchan;
+       struct l2cap_chan *chan;
        int err;
 
        if (!enable_6lowpan)
                return NULL;
 
-       pchan = chan_get();
-       if (!pchan)
+       chan = chan_create();
+       if (!chan)
                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) {
-               l2cap_chan_put(pchan);
+               l2cap_chan_put(chan);
                BT_ERR("psm cannot be added err %d", err);
                return NULL;
        }
 
-       return pchan;
+       return chan;
 }
 
 static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
index a7cdd99ec3f1688b62ccca001bf9522daff2b273..d2b3dd32d6cf1c6469b9fc728c62a625ac1c9b67 100644 (file)
@@ -134,6 +134,66 @@ static const struct file_operations dut_mode_fops = {
        .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 ---- */
 
 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)
                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);
        if (err < 0)
@@ -933,6 +988,9 @@ static int __hci_unconf_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
+       if (hci_dev_test_flag(hdev, HCI_SETUP))
+               hci_debugfs_create_basic(hdev);
+
        return 0;
 }
 
@@ -1385,6 +1443,9 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                goto done;
        }
 
+       set_bit(HCI_RUNNING, &hdev->flags);
+       hci_notify(hdev, HCI_DEV_OPEN);
+
        atomic_set(&hdev->cmd_cnt, 1);
        set_bit(HCI_INIT, &hdev->flags);
 
@@ -1466,6 +1527,9 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                        hdev->sent_cmd = NULL;
                }
 
+               clear_bit(HCI_RUNNING, &hdev->flags);
+               hci_notify(hdev, HCI_DEV_CLOSE);
+
                hdev->close(hdev);
                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)
 {
+       bool auto_off;
+
        BT_DBG("%s %p", hdev->name, hdev);
 
        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);
 
-       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_pend_le_actions_clear(hdev);
@@ -1626,9 +1692,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
        /* Reset device */
        skb_queue_purge(&hdev->cmd_q);
        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);
                __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT);
                clear_bit(HCI_INIT, &hdev->flags);
@@ -1649,6 +1714,9 @@ int hci_dev_do_close(struct hci_dev *hdev)
                hdev->sent_cmd = NULL;
        }
 
+       clear_bit(HCI_RUNNING, &hdev->flags);
+       hci_notify(hdev, HCI_DEV_CLOSE);
+
        /* After this point our queues are empty
         * and no tasks are scheduled. */
        hdev->close(hdev);
@@ -3471,6 +3539,13 @@ int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb)
                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 */
        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);
 
+/* 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 ---- */
 
 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. */
        skb_orphan(skb);
 
+       if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+               kfree_skb(skb);
+               return;
+       }
+
        err = hdev->send(hdev, skb);
        if (err < 0) {
                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;
 }
 
+/* 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 */
 static void hci_add_acl_hdr(struct sk_buff *skb, __u16 handle, __u16 flags)
 {
index 150556345263357a04a6bf2eb53382894a374349..9a100c1fd7b5ec13cdec4b852e70ca769700a067 100644 (file)
@@ -279,6 +279,9 @@ void hci_send_to_monitor(struct hci_dev *hdev, struct sk_buff *skb)
                else
                        opcode = cpu_to_le16(HCI_MON_SCO_TX_PKT);
                break;
+       case HCI_DIAG_PKT:
+               opcode = cpu_to_le16(HCI_MON_VENDOR_DIAG);
+               break;
        default:
                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_new_index *ni;
+       struct hci_mon_index_info *ii;
        struct sk_buff *skb;
        __le16 opcode;
 
@@ -312,7 +316,7 @@ static struct sk_buff *create_monitor_event(struct hci_dev *hdev, int event)
                if (!skb)
                        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->bus = hdev->bus;
                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);
                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:
                return NULL;
        }
@@ -358,6 +390,26 @@ static void send_monitor_replay(struct sock *sk)
 
                if (sock_queue_rcv_skb(sk, 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);
@@ -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)
 {
-       struct hci_ev_si_device ev;
-
        BT_DBG("hdev %s event %d", hdev->name, event);
 
-       /* Send event to monitor */
        if (atomic_read(&monitor_promisc)) {
                struct sk_buff *skb;
 
+               /* Send event to monitor */
                skb = create_monitor_event(hdev, event);
                if (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) {
                struct sock *sk;
index 8b4cdce3f62e5a4477a481c2fcc86c56be497321..aa4cf64e32a67b9263d0c5729b8f2f73289f22f1 100644 (file)
@@ -151,6 +151,22 @@ void bt_info(const char *format, ...)
 }
 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, ...)
 {
        struct va_format vaf;
index 9f0cfa598e3aaee92167f1cadfd8222edcb5853d..20c49c724ba04a3707f038a17774012ca5ba0421 100644 (file)
@@ -101,14 +101,9 @@ static const struct net_device_ops lowpan_netdev_ops = {
 
 static void lowpan_setup(struct net_device *ldev)
 {
-       ldev->addr_len          = 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->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;
        /* Set the lowpan hardware address to the wpan hardware address. */
        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);
 
index b1fd47d2802be684e18495de5f6a55cc77e0e5ee..65d55e05516c02310dfcf277d5024f2f3f0727d1 100644 (file)
@@ -29,6 +29,8 @@
 static int lowpan_give_skb_to_device(struct sk_buff *skb)
 {
        skb->protocol = htons(ETH_P_IPV6);
+       skb->dev->stats.rx_packets++;
+       skb->dev->stats.rx_bytes += skb->len;
 
        return netif_rx(skb);
 }
index 54939d031ea5d21e348a3be36bc9e2ef7bcaa82f..62a21f6f021e3ac7b8a40c6869d6397e99867dba 100644 (file)
@@ -10,6 +10,7 @@
 
 #include <net/6lowpan.h>
 #include <net/ieee802154_netdev.h>
+#include <net/mac802154.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));
 }
 
+/* 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,
                         unsigned short type, const void *_daddr,
                         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*
 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 sk_buff *frag;
        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);
 
        if (likely(frag)) {
                frag->dev = wdev;
                frag->priority = skb->priority;
-               skb_reserve(frag, wdev->hard_header_len);
+               skb_reserve(frag, wdev->needed_headroom);
                skb_reset_network_header(frag);
                *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 {
                frag = ERR_PTR(-ENOMEM);
@@ -103,13 +117,13 @@ lowpan_alloc_frag(struct sk_buff *skb, int size,
 static int
 lowpan_xmit_fragment(struct sk_buff *skb, const struct ieee802154_hdr *wpan_hdr,
                     u8 *frag_hdr, int frag_hdrlen,
-                    int offset, int len)
+                    int offset, int len, bool frag1)
 {
        struct sk_buff *frag;
 
        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))
                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,
                                  LOWPAN_FRAG1_HEAD_SIZE, 0,
-                                 frag_len + skb_network_header_len(skb));
+                                 frag_len + skb_network_header_len(skb),
+                                 true);
        if (rc) {
                pr_debug("%s unable to send FRAG1 packet (tag: %d)",
                         __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,
                                          LOWPAN_FRAGN_HEAD_SIZE, skb_offset,
-                                         frag_len);
+                                         frag_len, false);
                if (rc) {
                        pr_debug("%s unable to send a FRAGN packet. (tag: %d, offset: %d)\n",
                                 __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);
 
+       ldev->stats.tx_packets++;
+       ldev->stats.tx_bytes += dgram_size;
        consume_skb(skb);
        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;
        }
 
-       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)
@@ -240,6 +257,8 @@ netdev_tx_t lowpan_xmit(struct sk_buff *skb, struct net_device *ldev)
 
        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
         * 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) {
                skb->dev = lowpan_dev_info(ldev)->wdev;
+               ldev->stats.tx_packets++;
+               ldev->stats.tx_bytes += dgram_size;
                return dev_queue_xmit(skb);
        } else {
                netdev_tx_t rc;
index 1370d5b0041b952cf74c272c2494f8c0150c893b..188135bcb803531b4ef24f68113a729122336390 100644 (file)
@@ -12,6 +12,11 @@ menuconfig 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
        tristate "IEEE 802.15.4 socket interface"
        default y
index b0248e934230d166c4e61a66a0ad18c268fa0a71..c35fdfa6d04efbfb8a1c6f524e5afcdc58d83130 100644 (file)
@@ -95,6 +95,18 @@ cfg802154_rdev_by_wpan_phy_idx(int wpan_phy_idx)
        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 *
 wpan_phy_new(const struct cfg802154_ops *ops, size_t priv_size)
 {
index f3e95580caee0e96ada8bee0bb2a2e72aa21eda3..231fade959f39c1f2e1ce6c174b98306a08faa35 100644 (file)
@@ -42,5 +42,6 @@ extern int cfg802154_rdev_list_generation;
 void cfg802154_dev_free(struct cfg802154_registered_device *rdev);
 struct cfg802154_registered_device *
 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 */
index a051b69931779438b33f61456203c451167fa591..c7439f0fbbdf817b7baadc08cd20d825a57eb298 100644 (file)
@@ -83,35 +83,35 @@ ieee802154_hdr_push_sechdr(u8 *buf, const struct ieee802154_sechdr *hdr)
 }
 
 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 rc;
-       struct ieee802154_hdr_fc fc = hdr->fc;
+       struct ieee802154_hdr_fc *fc = &hdr->fc;
 
        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);
        if (rc < 0)
                return -EINVAL;
        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 &&
            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)
                return -EINVAL;
        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);
                if (rc < 0)
@@ -120,7 +120,7 @@ ieee802154_hdr_push(struct sk_buff *skb, const struct ieee802154_hdr *hdr)
                pos += rc;
        }
 
-       memcpy(buf, &fc, 2);
+       memcpy(buf, fc, 2);
 
        memcpy(skb_push(skb, pos), buf, pos);
 
index 3f89c0abdab170485a4bc83867c0f9609fe69c64..16ef0d9f566e8436e55f0263c51585ae5ab751b9 100644 (file)
@@ -232,8 +232,86 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
        [NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
 
        [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 */
 static inline void *nl802154hdr_put(struct sk_buff *skb, u32 portid, u32 seq,
                                    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);
 }
 
+#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
 nl802154_send_iface(struct sk_buff *msg, u32 portid, u32 seq, int flags,
                    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))
                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);
        return 0;
 
@@ -753,10 +937,8 @@ static int nl802154_new_interface(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
-       /* TODO add nla_get_le64 to netlink */
        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)
                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);
 }
 
-#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;
 }
 
-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,
        },
        {
@@ -1289,6 +2303,119 @@ static const struct genl_ops nl802154_ops[] = {
                .internal_flags = NL802154_FLAG_NEED_NETDEV |
                                  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 */
index 03b357501cc55dc99c29a642d89975cbd2da87a0..4441c63b3ea64829d1236fab96bf601dfa15cb24 100644 (file)
@@ -208,4 +208,113 @@ rdev_set_ackreq_default(struct cfg802154_registered_device *rdev,
        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 */
index b6eacf30ee7a619e10a682e7c3e5c2a5e9dfe408..a548be247e15d87c82cfda7a6cd3bde32a7159a7 100644 (file)
@@ -273,7 +273,7 @@ static int raw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
                goto out;
        }
 
-       mtu = dev->mtu;
+       mtu = IEEE802154_MTU;
        pr_debug("name = %s, mtu = %u\n", dev->name, mtu);
 
        if (size > mtu) {
@@ -637,7 +637,7 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
                err = -ENXIO;
                goto out;
        }
-       mtu = dev->mtu;
+       mtu = IEEE802154_MTU;
        pr_debug("name = %s, mtu = %u\n", dev->name, 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_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)
                goto out_skb;
 
index c865ebb2ace2b0b74cf3ba994c085790cde83da6..57b5e94471aff87b2581620c4d254163d2dcdea4 100644 (file)
@@ -266,6 +266,195 @@ ieee802154_set_ackreq_default(struct wpan_phy *wpan_phy,
        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 = {
        .add_virtual_intf_deprecated = ieee802154_add_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_lbt_mode = ieee802154_set_lbt_mode,
        .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 */
 };
index ed26952f9e143407723dca1ef45d8400424aca3e..7079cd32a7ad201265cf730027dda07254e622aa 100644 (file)
@@ -367,12 +367,11 @@ static int mac802154_set_header_security(struct ieee802154_sub_if_data *sdata,
        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_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;
 }
 
+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
 mac802154_header_parse(const struct sk_buff *skb, unsigned char *haddr)
 {
        struct ieee802154_hdr hdr;
-       struct ieee802154_addr *addr = (struct ieee802154_addr *)haddr;
 
        if (ieee802154_hdr_peek_addrs(skb, &hdr) < 0) {
                pr_debug("malformed packet\n");
                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 = {
@@ -471,9 +535,29 @@ static void ieee802154_if_setup(struct net_device *dev)
        dev->addr_len           = 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->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->ml_priv = &mac802154_mlme_wpan;
                wpan_dev->promiscuous_mode = false;
+               wpan_dev->header_ops = &ieee802154_header_ops;
 
                mutex_init(&sdata->sec_mtx);
 
@@ -550,7 +635,8 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
        if (!ndev)
                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);
        if (ret < 0)
index 985e9394e2afa97c069a9c565859a04c7a2cff51..7799d3c41fe2aa2f9e507579464bcacbcda7d645 100644 (file)
@@ -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_hw);
+       list_del_rcu(&pos->dev.list);
        call_rcu(&pos->rcu, llsec_dev_free_rcu);
 
        return 0;
index d1c33c1d6b9b3dcd74dc077b5de5b5556a18901e..42e96729dae69aac0362e791f9e42be962ae38b2 100644 (file)
@@ -87,6 +87,10 @@ ieee802154_subif_frame(struct ieee802154_sub_if_data *sdata,
 
        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);
        if (rc) {
                pr_debug("decryption failed: %i\n", rc);
index 7ed439172f30809d59fb5673131957ca8c25c56c..3827f359b336de356695635401820fc094344faf 100644 (file)
@@ -77,9 +77,6 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
                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. */
        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);
        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);
        if (rc) {
                netdev_warn(dev, "encryption failed: %i\n", rc);