Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
authorDavid S. Miller <davem@davemloft.net>
Thu, 3 Mar 2016 21:27:23 +0000 (16:27 -0500)
committerDavid S. Miller <davem@davemloft.net>
Thu, 3 Mar 2016 21:27:23 +0000 (16:27 -0500)
Johan Hedberg says:

====================
pull request: bluetooth-next 2016-03-01

Here's our main set of Bluetooth & 802.15.4 patches for the 4.6 kernel.

 - New Bluetooth HCI driver for Intel/AG6xx controllers
 - New Broadcom ACPI IDs
 - LED trigger support for indicating Bluetooth powered state
 - Various fixes in mac802154, 6lowpan and related drivers
 - New USB IDs for AR3012 Bluetooth controllers

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

Signed-off-by: David S. Miller <davem@davemloft.net>
26 files changed:
MAINTAINERS
drivers/bluetooth/Kconfig
drivers/bluetooth/Makefile
drivers/bluetooth/ath3k.c
drivers/bluetooth/btbcm.c
drivers/bluetooth/btusb.c
drivers/bluetooth/hci_ag6xx.c [new file with mode: 0644]
drivers/bluetooth/hci_bcm.c
drivers/bluetooth/hci_intel.c
drivers/bluetooth/hci_ldisc.c
drivers/bluetooth/hci_uart.h
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/mrf24j40.c
include/net/6lowpan.h
include/net/bluetooth/hci_core.h
include/net/mac802154.h
net/6lowpan/core.c
net/6lowpan/debugfs.c
net/6lowpan/iphc.c
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/hci_core.c
net/bluetooth/leds.c [new file with mode: 0644]
net/bluetooth/leds.h [new file with mode: 0644]
net/ieee802154/6lowpan/core.c
net/mac802154/main.c

index 9c4d157f3ddbdacf60b9542dfe87632b6164160c..f5d772686ddfd093feb63d506af0a5c91ff63e20 100644 (file)
@@ -151,7 +151,7 @@ S:  Maintained
 F:     drivers/scsi/53c700*
 
 6LOWPAN GENERIC (BTLE/IEEE 802.15.4)
-M:     Alexander Aring <alex.aring@gmail.com>
+M:     Alexander Aring <aar@pengutronix.de>
 M:     Jukka Rissanen <jukka.rissanen@linux.intel.com>
 L:     linux-bluetooth@vger.kernel.org
 L:     linux-wpan@vger.kernel.org
@@ -5424,10 +5424,11 @@ S:      Supported
 F:     drivers/idle/i7300_idle.c
 
 IEEE 802.15.4 SUBSYSTEM
-M:     Alexander Aring <alex.aring@gmail.com>
+M:     Alexander Aring <aar@pengutronix.de>
 L:     linux-wpan@vger.kernel.org
-W:     https://github.com/linux-wpan
-T:     git git://github.com/linux-wpan/linux-wpan-next.git
+W:     http://wpan.cakelab.org/
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth.git
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git
 S:     Maintained
 F:     net/ieee802154/
 F:     net/mac802154/
index ec6af15950622ef2448cfda759fd73bb141286f0..cf50fd2e96df8d886580e9f26885b6f5b4b18c2c 100644 (file)
@@ -169,6 +169,17 @@ config BT_HCIUART_QCA
 
          Say Y here to compile support for QCA protocol.
 
+config BT_HCIUART_AG6XX
+       bool "Intel AG6XX protocol support"
+       depends on BT_HCIUART
+       select BT_HCIUART_H4
+       select BT_INTEL
+       help
+         The Intel/AG6XX protocol support enables Bluetooth HCI over serial
+         port interface for Intel ibt 2.1 Bluetooth controllers.
+
+         Say Y here to compile support for Intel AG6XX protocol.
+
 config BT_HCIBCM203X
        tristate "HCI BCM203x USB driver"
        depends on USB
index 07c9cf381e5aeb2585a18beb0f9aa98a75862e9e..9c18939fc5c98fe02d0bfa039331c62f30d2632b 100644 (file)
@@ -36,6 +36,7 @@ hci_uart-$(CONFIG_BT_HCIUART_3WIRE)   += hci_h5.o
 hci_uart-$(CONFIG_BT_HCIUART_INTEL)    += hci_intel.o
 hci_uart-$(CONFIG_BT_HCIUART_BCM)      += hci_bcm.o
 hci_uart-$(CONFIG_BT_HCIUART_QCA)      += hci_qca.o
+hci_uart-$(CONFIG_BT_HCIUART_AG6XX)    += hci_ag6xx.o
 hci_uart-objs                          := $(hci_uart-y)
 
 ccflags-y += -D__CHECK_ENDIAN__
index fa893c3ec4087f39382f3dc83b968c9859f41cca..93747389dd289d99961bd88d3126a4a2c5d3764a 100644 (file)
@@ -82,6 +82,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x0489, 0xe05f) },
        { USB_DEVICE(0x0489, 0xe076) },
        { USB_DEVICE(0x0489, 0xe078) },
+       { USB_DEVICE(0x0489, 0xe095) },
        { USB_DEVICE(0x04c5, 0x1330) },
        { USB_DEVICE(0x04CA, 0x3004) },
        { USB_DEVICE(0x04CA, 0x3005) },
@@ -92,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x04CA, 0x300d) },
        { USB_DEVICE(0x04CA, 0x300f) },
        { USB_DEVICE(0x04CA, 0x3010) },
+       { USB_DEVICE(0x04CA, 0x3014) },
        { USB_DEVICE(0x0930, 0x0219) },
        { USB_DEVICE(0x0930, 0x021c) },
        { USB_DEVICE(0x0930, 0x0220) },
@@ -113,6 +115,7 @@ static const struct usb_device_id ath3k_table[] = {
        { USB_DEVICE(0x13d3, 0x3362) },
        { USB_DEVICE(0x13d3, 0x3375) },
        { USB_DEVICE(0x13d3, 0x3393) },
+       { USB_DEVICE(0x13d3, 0x3395) },
        { USB_DEVICE(0x13d3, 0x3402) },
        { USB_DEVICE(0x13d3, 0x3408) },
        { USB_DEVICE(0x13d3, 0x3423) },
@@ -144,6 +147,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
@@ -154,6 +158,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -175,6 +180,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
        { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
@@ -497,6 +503,7 @@ static int ath3k_probe(struct usb_interface *intf,
        /* match device ID in ath3k blacklist table */
        if (!id->driver_info) {
                const struct usb_device_id *match;
+
                match = usb_match_id(intf, ath3k_blist_tbl);
                if (match)
                        id = match;
index 0b697946e9bc7d91abf76b0dbfa7384fec4ce9b8..fdb44829ab6ff7009597a1b0080b741bcd09b9b9 100644 (file)
@@ -467,7 +467,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        err = request_firmware(&fw, fw_name, &hdev->dev);
        if (err < 0) {
                BT_INFO("%s: BCM: Patch %s not found", hdev->name, fw_name);
-               return 0;
+               goto done;
        }
 
        btbcm_patchram(hdev, fw);
@@ -501,6 +501,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
        BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
        kfree_skb(skb);
 
+done:
        btbcm_check_bdaddr(hdev);
 
        set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
index a191e318fab88a92fa52641b3d27d1ea94ced0ce..97f3bba93a8e2c5c010fa234321caaeca3737cf4 100644 (file)
@@ -196,6 +196,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0489, 0xe078), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x0489, 0xe095), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
@@ -206,6 +207,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x04ca, 0x3010), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x04ca, 0x3014), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
@@ -227,6 +229,7 @@ static const struct usb_device_id blacklist_table[] = {
        { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 },
+       { USB_DEVICE(0x13d3, 0x3395), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
        { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
diff --git a/drivers/bluetooth/hci_ag6xx.c b/drivers/bluetooth/hci_ag6xx.c
new file mode 100644 (file)
index 0000000..6923d17
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ *
+ *  Bluetooth HCI UART driver for Intel/AG6xx devices
+ *
+ *  Copyright (C) 2016  Intel Corporation
+ *
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_uart.h"
+#include "btintel.h"
+
+struct ag6xx_data {
+       struct sk_buff *rx_skb;
+       struct sk_buff_head txq;
+};
+
+struct pbn_entry {
+       __le32 addr;
+       __le32 plen;
+       __u8 data[0];
+} __packed;
+
+static int ag6xx_open(struct hci_uart *hu)
+{
+       struct ag6xx_data *ag6xx;
+
+       BT_DBG("hu %p", hu);
+
+       ag6xx = kzalloc(sizeof(*ag6xx), GFP_KERNEL);
+       if (!ag6xx)
+               return -ENOMEM;
+
+       skb_queue_head_init(&ag6xx->txq);
+
+       hu->priv = ag6xx;
+       return 0;
+}
+
+static int ag6xx_close(struct hci_uart *hu)
+{
+       struct ag6xx_data *ag6xx = hu->priv;
+
+       BT_DBG("hu %p", hu);
+
+       skb_queue_purge(&ag6xx->txq);
+       kfree_skb(ag6xx->rx_skb);
+       kfree(ag6xx);
+
+       hu->priv = NULL;
+       return 0;
+}
+
+static int ag6xx_flush(struct hci_uart *hu)
+{
+       struct ag6xx_data *ag6xx = hu->priv;
+
+       BT_DBG("hu %p", hu);
+
+       skb_queue_purge(&ag6xx->txq);
+       return 0;
+}
+
+static struct sk_buff *ag6xx_dequeue(struct hci_uart *hu)
+{
+       struct ag6xx_data *ag6xx = hu->priv;
+       struct sk_buff *skb;
+
+       skb = skb_dequeue(&ag6xx->txq);
+       if (!skb)
+               return skb;
+
+       /* Prepend skb with frame type */
+       memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
+       return skb;
+}
+
+static int ag6xx_enqueue(struct hci_uart *hu, struct sk_buff *skb)
+{
+       struct ag6xx_data *ag6xx = hu->priv;
+
+       skb_queue_tail(&ag6xx->txq, skb);
+       return 0;
+}
+
+static const struct h4_recv_pkt ag6xx_recv_pkts[] = {
+       { H4_RECV_ACL,    .recv = hci_recv_frame   },
+       { H4_RECV_SCO,    .recv = hci_recv_frame   },
+       { H4_RECV_EVENT,  .recv = hci_recv_frame   },
+};
+
+static int ag6xx_recv(struct hci_uart *hu, const void *data, int count)
+{
+       struct ag6xx_data *ag6xx = hu->priv;
+
+       if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
+               return -EUNATCH;
+
+       ag6xx->rx_skb = h4_recv_buf(hu->hdev, ag6xx->rx_skb, data, count,
+                                   ag6xx_recv_pkts,
+                                   ARRAY_SIZE(ag6xx_recv_pkts));
+       if (IS_ERR(ag6xx->rx_skb)) {
+               int err = PTR_ERR(ag6xx->rx_skb);
+               bt_dev_err(hu->hdev, "Frame reassembly failed (%d)", err);
+               ag6xx->rx_skb = NULL;
+               return err;
+       }
+
+       return count;
+}
+
+static int intel_mem_write(struct hci_dev *hdev, u32 addr, u32 plen,
+                          const void *data)
+{
+       /* Can write a maximum of 247 bytes per HCI command.
+        * HCI cmd Header (3), Intel mem write header (6), data (247).
+        */
+       while (plen > 0) {
+               struct sk_buff *skb;
+               u8 cmd_param[253], fragment_len = (plen > 247) ? 247 : plen;
+               __le32 leaddr = cpu_to_le32(addr);
+
+               memcpy(cmd_param, &leaddr, 4);
+               cmd_param[4] = 0;
+               cmd_param[5] = fragment_len;
+               memcpy(cmd_param + 6, data, fragment_len);
+
+               skb = __hci_cmd_sync(hdev, 0xfc8e, fragment_len + 6, cmd_param,
+                                    HCI_INIT_TIMEOUT);
+               if (IS_ERR(skb))
+                       return PTR_ERR(skb);
+               kfree_skb(skb);
+
+               plen -= fragment_len;
+               data += fragment_len;
+               addr += fragment_len;
+       }
+
+       return 0;
+}
+
+static int ag6xx_setup(struct hci_uart *hu)
+{
+       struct hci_dev *hdev = hu->hdev;
+       struct sk_buff *skb;
+       struct intel_version ver;
+       const struct firmware *fw;
+       const u8 *fw_ptr;
+       char fwname[64];
+       bool patched = false;
+       int err;
+
+       hu->hdev->set_diag = btintel_set_diag;
+       hu->hdev->set_bdaddr = btintel_set_bdaddr;
+
+       err = btintel_enter_mfg(hdev);
+       if (err)
+               return err;
+
+       err = btintel_read_version(hdev, &ver);
+       if (err)
+               return err;
+
+       btintel_version_info(hdev, &ver);
+
+       /* The hardware platform number has a fixed value of 0x37 and
+        * for now only accept this single value.
+        */
+       if (ver.hw_platform != 0x37) {
+               bt_dev_err(hdev, "Unsupported Intel hardware platform: 0x%X",
+                          ver.hw_platform);
+               return -EINVAL;
+       }
+
+       /* Only the hardware variant iBT 2.1 (AG6XX) is supported by this
+        * firmware setup method.
+        */
+       if (ver.hw_variant != 0x0a) {
+               bt_dev_err(hdev, "Unsupported Intel hardware variant: 0x%x",
+                          ver.hw_variant);
+               return -EINVAL;
+       }
+
+       snprintf(fwname, sizeof(fwname), "intel/ibt-hw-%x.%x.bddata",
+                ver.hw_platform, ver.hw_variant);
+
+       err = request_firmware(&fw, fwname, &hdev->dev);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to open Intel bddata file: %s (%d)",
+                          fwname, err);
+               goto patch;
+       }
+       fw_ptr = fw->data;
+
+       bt_dev_info(hdev, "Applying bddata (%s)", fwname);
+
+       skb = __hci_cmd_sync_ev(hdev, 0xfc2f, fw->size, fw->data,
+                               HCI_EV_CMD_STATUS, HCI_CMD_TIMEOUT);
+       if (IS_ERR(skb)) {
+               bt_dev_err(hdev, "Applying bddata failed (%ld)", PTR_ERR(skb));
+               release_firmware(fw);
+               return PTR_ERR(skb);
+       }
+       kfree_skb(skb);
+
+       release_firmware(fw);
+
+patch:
+       /* If there is no applied patch, fw_patch_num is always 0x00. In other
+        * cases, current firmware is already patched. No need to patch it.
+        */
+       if (ver.fw_patch_num) {
+               bt_dev_info(hdev, "Device is already patched. patch num: %02x",
+                           ver.fw_patch_num);
+               patched = true;
+               goto complete;
+       }
+
+       snprintf(fwname, sizeof(fwname),
+                "intel/ibt-hw-%x.%x.%x-fw-%x.%x.%x.%x.%x.pbn",
+                ver.hw_platform, ver.hw_variant, ver.hw_revision,
+                ver.fw_variant,  ver.fw_revision, ver.fw_build_num,
+                ver.fw_build_ww, ver.fw_build_yy);
+
+       err = request_firmware(&fw, fwname, &hdev->dev);
+       if (err < 0) {
+               bt_dev_err(hdev, "Failed to open Intel patch file: %s(%d)",
+                          fwname, err);
+               goto complete;
+       }
+       fw_ptr = fw->data;
+
+       bt_dev_info(hdev, "Patching firmware file (%s)", fwname);
+
+       /* PBN patch file contains a list of binary patches to be applied on top
+        * of the embedded firmware. Each patch entry header contains the target
+        * address and patch size.
+        *
+        * Patch entry:
+        * | addr(le) | patch_len(le) | patch_data |
+        * | 4 Bytes  |    4 Bytes    |   n Bytes  |
+        *
+        * PBN file is terminated by a patch entry whose address is 0xffffffff.
+        */
+       while (fw->size > fw_ptr - fw->data) {
+               struct pbn_entry *pbn = (void *)fw_ptr;
+               u32 addr, plen;
+
+               if (pbn->addr == 0xffffffff) {
+                       bt_dev_info(hdev, "Patching complete");
+                       patched = true;
+                       break;
+               }
+
+               addr = le32_to_cpu(pbn->addr);
+               plen = le32_to_cpu(pbn->plen);
+
+               if (fw->data + fw->size <= pbn->data + plen) {
+                       bt_dev_info(hdev, "Invalid patch len (%d)", plen);
+                       break;
+               }
+
+               bt_dev_info(hdev, "Patching %td/%zu", (fw_ptr - fw->data),
+                           fw->size);
+
+               err = intel_mem_write(hdev, addr, plen, pbn->data);
+               if (err) {
+                       bt_dev_err(hdev, "Patching failed");
+                       break;
+               }
+
+               fw_ptr = pbn->data + plen;
+       }
+
+       release_firmware(fw);
+
+complete:
+       /* Exit manufacturing mode and reset */
+       err = btintel_exit_mfg(hdev, true, patched);
+       if (err)
+               return err;
+
+       /* Set the event mask for Intel specific vendor events. This enables
+        * a few extra events that are useful during general operation.
+        */
+       btintel_set_event_mask_mfg(hdev, false);
+
+       btintel_check_bdaddr(hdev);
+       return 0;
+}
+
+static const struct hci_uart_proto ag6xx_proto = {
+       .id             = HCI_UART_AG6XX,
+       .name           = "AG6XX",
+       .manufacturer   = 2,
+       .open           = ag6xx_open,
+       .close          = ag6xx_close,
+       .flush          = ag6xx_flush,
+       .setup          = ag6xx_setup,
+       .recv           = ag6xx_recv,
+       .enqueue        = ag6xx_enqueue,
+       .dequeue        = ag6xx_dequeue,
+};
+
+int __init ag6xx_init(void)
+{
+       return hci_uart_register_proto(&ag6xx_proto);
+}
+
+int __exit ag6xx_deinit(void)
+{
+       return hci_uart_unregister_proto(&ag6xx_proto);
+}
index 5f3de181e7443632746ef77d6ee73ec9a26494fa..bb4c5a00aea076059d4114ddb7f23466ba70c8c2 100644 (file)
@@ -820,10 +820,12 @@ static const struct acpi_device_id bcm_acpi_match[] = {
        { "BCM2E3D", 0 },
        { "BCM2E3F", 0 },
        { "BCM2E40", 0 },
+       { "BCM2E54", 0 },
        { "BCM2E64", 0 },
        { "BCM2E65", 0 },
        { "BCM2E67", 0 },
        { "BCM2E7B", 0 },
+       { "BCM2E7C", 0 },
        { },
 };
 MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
index 3d63ea37bd4ca683b37c7ea7661be70163b6590c..91d605147b10e45a721941582ac366185afb0da3 100644 (file)
@@ -488,7 +488,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
        clear_bit(STATE_BOOTING, &intel->flags);
 
        /* In case of timeout, try to continue anyway */
-       if (err && err != ETIMEDOUT)
+       if (err && err != -ETIMEDOUT)
                return err;
 
        bt_dev_info(hdev, "Change controller speed to %d", speed);
@@ -581,7 +581,7 @@ static int intel_setup(struct hci_uart *hu)
        clear_bit(STATE_BOOTING, &intel->flags);
 
        /* In case of timeout, try to continue anyway */
-       if (err && err != ETIMEDOUT)
+       if (err && err != -ETIMEDOUT)
                return err;
 
        set_bit(STATE_BOOTLOADER, &intel->flags);
index 73202624133b4e180a45c75f12ae329cc934bac0..c00168a5bb800f3e74f6924f197e668020f02eb9 100644 (file)
@@ -804,6 +804,9 @@ static int __init hci_uart_init(void)
 #ifdef CONFIG_BT_HCIUART_QCA
        qca_init();
 #endif
+#ifdef CONFIG_BT_HCIUART_AG6XX
+       ag6xx_init();
+#endif
 
        return 0;
 }
@@ -836,6 +839,9 @@ static void __exit hci_uart_exit(void)
 #ifdef CONFIG_BT_HCIUART_QCA
        qca_deinit();
 #endif
+#ifdef CONFIG_BT_HCIUART_AG6XX
+       ag6xx_deinit();
+#endif
 
        /* Release tty registration of line discipline */
        err = tty_unregister_ldisc(N_HCI);
index 82c92f1b65b4af7c317c8a0af91723a481ac3874..4814ff08f4270156c70aaced894ec0cb2a5d5f63 100644 (file)
@@ -35,7 +35,7 @@
 #define HCIUARTGETFLAGS                _IOR('U', 204, int)
 
 /* UART protocols */
-#define HCI_UART_MAX_PROTO     9
+#define HCI_UART_MAX_PROTO     10
 
 #define HCI_UART_H4    0
 #define HCI_UART_BCSP  1
@@ -46,6 +46,7 @@
 #define HCI_UART_INTEL 6
 #define HCI_UART_BCM   7
 #define HCI_UART_QCA   8
+#define HCI_UART_AG6XX 9
 
 #define HCI_UART_RAW_DEVICE    0
 #define HCI_UART_RESET_ON_INIT 1
@@ -182,3 +183,8 @@ int bcm_deinit(void);
 int qca_init(void);
 int qca_deinit(void);
 #endif
+
+#ifdef CONFIG_BT_HCIUART_AG6XX
+int ag6xx_init(void);
+int ag6xx_deinit(void);
+#endif
index 0fbbba7a0cae38afe5fc6dfa122489c1a3271fdf..cb9e9fe6d77a0b75a8d0f88f523b4f6059830dcc 100644 (file)
@@ -343,16 +343,26 @@ static const struct regmap_config at86rf230_regmap_spi_config = {
 };
 
 static void
-at86rf230_async_error_recover(void *context)
+at86rf230_async_error_recover_complete(void *context)
 {
        struct at86rf230_state_change *ctx = context;
        struct at86rf230_local *lp = ctx->lp;
 
-       lp->is_tx = 0;
-       at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
-       ieee802154_wake_queue(lp->hw);
        if (ctx->free)
                kfree(ctx);
+
+       ieee802154_wake_queue(lp->hw);
+}
+
+static void
+at86rf230_async_error_recover(void *context)
+{
+       struct at86rf230_state_change *ctx = context;
+       struct at86rf230_local *lp = ctx->lp;
+
+       lp->is_tx = 0;
+       at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON,
+                                    at86rf230_async_error_recover_complete);
 }
 
 static inline void
@@ -892,14 +902,12 @@ at86rf230_xmit_start(void *context)
        struct at86rf230_local *lp = ctx->lp;
 
        /* check if we change from off state */
-       if (lp->is_tx_from_off) {
-               lp->is_tx_from_off = false;
+       if (lp->is_tx_from_off)
                at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
                                             at86rf230_write_frame);
-       } else {
+       else
                at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
                                             at86rf230_xmit_tx_on);
-       }
 }
 
 static int
@@ -923,6 +931,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
                at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
                                             at86rf230_xmit_start);
        } else {
+               lp->is_tx_from_off = false;
                at86rf230_xmit_start(ctx);
        }
 
index 4cdf51638972095644204eebccf46c8c77421ef2..764a2bddfaee390e936fed48eab0487b8ac9766a 100644 (file)
@@ -310,6 +310,7 @@ mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg)
        case REG_TRISGPIO:
        case REG_GPIO:
        case REG_RFCTL:
+       case REG_SECCR2:
        case REG_SLPACK:
        case REG_BBREG0:
        case REG_BBREG1:
index 2f6a3f2233edf8e2052c7c5bd5856894bbe7d5eb..da3a77d25fcbe1d32fcf9fff0659e48612a0e0c1 100644 (file)
@@ -75,6 +75,8 @@
 #define LOWPAN_IPHC_MAX_HC_BUF_LEN     (sizeof(struct ipv6hdr) +       \
                                         LOWPAN_IPHC_MAX_HEADER_LEN +   \
                                         LOWPAN_NHC_MAX_HDR_LEN)
+/* SCI/DCI is 4 bit width, so we have maximum 16 entries */
+#define LOWPAN_IPHC_CTX_TABLE_SIZE     (1 << 4)
 
 #define LOWPAN_DISPATCH_IPV6           0x41 /* 01000001 = 65 */
 #define LOWPAN_DISPATCH_IPHC           0x60 /* 011xxxxx = ... */
@@ -98,9 +100,39 @@ enum lowpan_lltypes {
        LOWPAN_LLTYPE_IEEE802154,
 };
 
+enum lowpan_iphc_ctx_flags {
+       LOWPAN_IPHC_CTX_FLAG_ACTIVE,
+       LOWPAN_IPHC_CTX_FLAG_COMPRESSION,
+};
+
+struct lowpan_iphc_ctx {
+       u8 id;
+       struct in6_addr pfx;
+       u8 plen;
+       unsigned long flags;
+};
+
+struct lowpan_iphc_ctx_table {
+       spinlock_t lock;
+       const struct lowpan_iphc_ctx_ops *ops;
+       struct lowpan_iphc_ctx table[LOWPAN_IPHC_CTX_TABLE_SIZE];
+};
+
+static inline bool lowpan_iphc_ctx_is_active(const struct lowpan_iphc_ctx *ctx)
+{
+       return test_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
+}
+
+static inline bool
+lowpan_iphc_ctx_is_compression(const struct lowpan_iphc_ctx *ctx)
+{
+       return test_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
+}
+
 struct lowpan_priv {
        enum lowpan_lltypes lltype;
        struct dentry *iface_debugfs;
+       struct lowpan_iphc_ctx_table ctx;
 
        /* must be last */
        u8 priv[0] __aligned(sizeof(void *));
index d4f82edb5cffe0528719dc416e8b28b71460e485..dc71473462ac9f60680dad7c7cce8e9878b1a8a5 100644 (file)
@@ -25,6 +25,7 @@
 #ifndef __HCI_CORE_H
 #define __HCI_CORE_H
 
+#include <linux/leds.h>
 #include <net/bluetooth/hci.h>
 #include <net/bluetooth/hci_sock.h>
 
@@ -396,6 +397,8 @@ struct hci_dev {
        struct delayed_work     rpa_expired;
        bdaddr_t                rpa;
 
+       struct led_trigger      *power_led;
+
        int (*open)(struct hci_dev *hdev);
        int (*close)(struct hci_dev *hdev);
        int (*flush)(struct hci_dev *hdev);
index da574bbdc33393fc927ba7297f7d67d0cf59b06e..2e3cdd2048d2bee45e9d248459b80db5a7d8658e 100644 (file)
@@ -247,8 +247,9 @@ 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->len < 2)) {
+       /* check if we can fc at skb_mac_header of sk buffer */
+       if (unlikely(!skb_mac_header_was_set(skb) ||
+                    (skb_tail_pointer(skb) - skb_mac_header(skb)) < 2)) {
                WARN_ON(1);
                return cpu_to_le16(0);
        }
index faf65baed617d5c5b39d9cadd5ef0264ea546d59..34e44c0c08368eb5699dd101c4a2c3f35adadfab 100644 (file)
@@ -20,7 +20,7 @@
 int lowpan_register_netdevice(struct net_device *dev,
                              enum lowpan_lltypes lltype)
 {
-       int ret;
+       int i, ret;
 
        dev->addr_len = EUI64_ADDR_LEN;
        dev->type = ARPHRD_6LOWPAN;
@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,
 
        lowpan_priv(dev)->lltype = lltype;
 
+       spin_lock_init(&lowpan_priv(dev)->ctx.lock);
+       for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
+               lowpan_priv(dev)->ctx.table[i].id = i;
+
        ret = register_netdevice(dev);
        if (ret < 0)
                return ret;
@@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
 }
 EXPORT_SYMBOL(lowpan_unregister_netdev);
 
+static int lowpan_event(struct notifier_block *unused,
+                       unsigned long event, void *ptr)
+{
+       struct net_device *dev = netdev_notifier_info_to_dev(ptr);
+       int i;
+
+       if (dev->type != ARPHRD_6LOWPAN)
+               return NOTIFY_DONE;
+
+       switch (event) {
+       case NETDEV_DOWN:
+               for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++)
+                       clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE,
+                                 &lowpan_priv(dev)->ctx.table[i].flags);
+               break;
+       default:
+               return NOTIFY_DONE;
+       }
+
+       return NOTIFY_OK;
+}
+
+static struct notifier_block lowpan_notifier = {
+       .notifier_call = lowpan_event,
+};
+
 static int __init lowpan_module_init(void)
 {
        int ret;
@@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
        if (ret < 0)
                return ret;
 
+       ret = register_netdevice_notifier(&lowpan_notifier);
+       if (ret < 0) {
+               lowpan_debugfs_exit();
+               return ret;
+       }
+
        request_module_nowait("ipv6");
 
        request_module_nowait("nhc_dest");
@@ -92,6 +128,7 @@ static int __init lowpan_module_init(void)
 static void __exit lowpan_module_exit(void)
 {
        lowpan_debugfs_exit();
+       unregister_netdevice_notifier(&lowpan_notifier);
 }
 
 module_init(lowpan_module_init);
index 88eef84df0fc124275e83f58ff78248ffe389a5c..aa49ff4ce6fda9886d10c90b45ae96fdb66f41f0 100644 (file)
 
 #include "6lowpan_i.h"
 
+#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS        8
+
 static struct dentry *lowpan_debugfs;
 
+static int lowpan_ctx_flag_active_set(void *data, u64 val)
+{
+       struct lowpan_iphc_ctx *ctx = data;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       if (val)
+               set_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
+       else
+               clear_bit(LOWPAN_IPHC_CTX_FLAG_ACTIVE, &ctx->flags);
+
+       return 0;
+}
+
+static int lowpan_ctx_flag_active_get(void *data, u64 *val)
+{
+       *val = lowpan_iphc_ctx_is_active(data);
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_active_fops,
+                       lowpan_ctx_flag_active_get,
+                       lowpan_ctx_flag_active_set, "%llu\n");
+
+static int lowpan_ctx_flag_c_set(void *data, u64 val)
+{
+       struct lowpan_iphc_ctx *ctx = data;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       if (val)
+               set_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
+       else
+               clear_bit(LOWPAN_IPHC_CTX_FLAG_COMPRESSION, &ctx->flags);
+
+       return 0;
+}
+
+static int lowpan_ctx_flag_c_get(void *data, u64 *val)
+{
+       *val = lowpan_iphc_ctx_is_compression(data);
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_flag_c_fops, lowpan_ctx_flag_c_get,
+                       lowpan_ctx_flag_c_set, "%llu\n");
+
+static int lowpan_ctx_plen_set(void *data, u64 val)
+{
+       struct lowpan_iphc_ctx *ctx = data;
+       struct lowpan_iphc_ctx_table *t =
+               container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+
+       if (val > 128)
+               return -EINVAL;
+
+       spin_lock_bh(&t->lock);
+       ctx->plen = val;
+       spin_unlock_bh(&t->lock);
+
+       return 0;
+}
+
+static int lowpan_ctx_plen_get(void *data, u64 *val)
+{
+       struct lowpan_iphc_ctx *ctx = data;
+       struct lowpan_iphc_ctx_table *t =
+               container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+
+       spin_lock_bh(&t->lock);
+       *val = ctx->plen;
+       spin_unlock_bh(&t->lock);
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(lowpan_ctx_plen_fops, lowpan_ctx_plen_get,
+                       lowpan_ctx_plen_set, "%llu\n");
+
+static int lowpan_ctx_pfx_show(struct seq_file *file, void *offset)
+{
+       struct lowpan_iphc_ctx *ctx = file->private;
+       struct lowpan_iphc_ctx_table *t =
+               container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+
+       spin_lock_bh(&t->lock);
+       seq_printf(file, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\n",
+                  be16_to_cpu(ctx->pfx.s6_addr16[0]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[1]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[2]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[3]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[4]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[5]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[6]),
+                  be16_to_cpu(ctx->pfx.s6_addr16[7]));
+       spin_unlock_bh(&t->lock);
+
+       return 0;
+}
+
+static int lowpan_ctx_pfx_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, lowpan_ctx_pfx_show, inode->i_private);
+}
+
+static ssize_t lowpan_ctx_pfx_write(struct file *fp,
+                                   const char __user *user_buf, size_t count,
+                                   loff_t *ppos)
+{
+       char buf[128] = {};
+       struct seq_file *file = fp->private_data;
+       struct lowpan_iphc_ctx *ctx = file->private;
+       struct lowpan_iphc_ctx_table *t =
+               container_of(ctx, struct lowpan_iphc_ctx_table, table[ctx->id]);
+       int status = count, n, i;
+       unsigned int addr[8];
+
+       if (copy_from_user(&buf, user_buf, min_t(size_t, sizeof(buf) - 1,
+                                                count))) {
+               status = -EFAULT;
+               goto out;
+       }
+
+       n = sscanf(buf, "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x",
+                  &addr[0], &addr[1], &addr[2], &addr[3], &addr[4],
+                  &addr[5], &addr[6], &addr[7]);
+       if (n != LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS) {
+               status = -EINVAL;
+               goto out;
+       }
+
+       spin_lock_bh(&t->lock);
+       for (i = 0; i < 8; i++)
+               ctx->pfx.s6_addr16[i] = cpu_to_be16(addr[i] & 0xffff);
+       spin_unlock_bh(&t->lock);
+
+out:
+       return status;
+}
+
+const struct file_operations lowpan_ctx_pfx_fops = {
+       .open           = lowpan_ctx_pfx_open,
+       .read           = seq_read,
+       .write          = lowpan_ctx_pfx_write,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int lowpan_dev_debugfs_ctx_init(struct net_device *dev,
+                                      struct dentry *ctx, u8 id)
+{
+       struct lowpan_priv *lpriv = lowpan_priv(dev);
+       struct dentry *dentry, *root;
+       char buf[32];
+
+       WARN_ON_ONCE(id > LOWPAN_IPHC_CTX_TABLE_SIZE);
+
+       sprintf(buf, "%d", id);
+
+       root = debugfs_create_dir(buf, ctx);
+       if (!root)
+               return -EINVAL;
+
+       dentry = debugfs_create_file("active", 0644, root,
+                                    &lpriv->ctx.table[id],
+                                    &lowpan_ctx_flag_active_fops);
+       if (!dentry)
+               return -EINVAL;
+
+       dentry = debugfs_create_file("compression", 0644, root,
+                                    &lpriv->ctx.table[id],
+                                    &lowpan_ctx_flag_c_fops);
+       if (!dentry)
+               return -EINVAL;
+
+       dentry = debugfs_create_file("prefix", 0644, root,
+                                    &lpriv->ctx.table[id],
+                                    &lowpan_ctx_pfx_fops);
+       if (!dentry)
+               return -EINVAL;
+
+       dentry = debugfs_create_file("prefix_len", 0644, root,
+                                    &lpriv->ctx.table[id],
+                                    &lowpan_ctx_plen_fops);
+       if (!dentry)
+               return -EINVAL;
+
+       return 0;
+}
+
+static int lowpan_context_show(struct seq_file *file, void *offset)
+{
+       struct lowpan_iphc_ctx_table *t = file->private;
+       int i;
+
+       seq_printf(file, "%3s|%-43s|%c\n", "cid", "prefix", 'C');
+       seq_puts(file, "-------------------------------------------------\n");
+
+       spin_lock_bh(&t->lock);
+       for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+               if (!lowpan_iphc_ctx_is_active(&t->table[i]))
+                       continue;
+
+               seq_printf(file, "%3d|%39pI6c/%-3d|%d\n", t->table[i].id,
+                          &t->table[i].pfx, t->table[i].plen,
+                          lowpan_iphc_ctx_is_compression(&t->table[i]));
+       }
+       spin_unlock_bh(&t->lock);
+
+       return 0;
+}
+
+static int lowpan_context_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, lowpan_context_show, inode->i_private);
+}
+
+const struct file_operations lowpan_context_fops = {
+       .open           = lowpan_context_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 int lowpan_dev_debugfs_init(struct net_device *dev)
 {
        struct lowpan_priv *lpriv = lowpan_priv(dev);
+       struct dentry *contexts, *dentry;
+       int ret, i;
 
        /* creating the root */
        lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
        if (!lpriv->iface_debugfs)
                goto fail;
 
+       contexts = debugfs_create_dir("contexts", lpriv->iface_debugfs);
+       if (!contexts)
+               goto remove_root;
+
+       dentry = debugfs_create_file("show", 0644, contexts,
+                                    &lowpan_priv(dev)->ctx,
+                                    &lowpan_context_fops);
+       if (!dentry)
+               goto remove_root;
+
+       for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+               ret = lowpan_dev_debugfs_ctx_init(dev, contexts, i);
+               if (ret < 0)
+                       goto remove_root;
+       }
+
        return 0;
 
+remove_root:
+       lowpan_dev_debugfs_exit(dev);
 fail:
        return -EINVAL;
 }
index 346b5c1a91851efd16b22695a7a1259c1cca9139..72172514fea063a34ed2e1314d1a996db6a38015 100644 (file)
@@ -56,6 +56,7 @@
 /* special link-layer handling */
 #include <net/mac802154.h>
 
+#include "6lowpan_i.h"
 #include "nhc.h"
 
 /* Values of fields within the IPHC encoding first byte */
         (((a)->s6_addr16[6]) == 0) &&          \
         (((a)->s6_addr[14]) == 0))
 
+#define LOWPAN_IPHC_CID_DCI(cid)       (cid & 0x0f)
+#define LOWPAN_IPHC_CID_SCI(cid)       ((cid & 0xf0) >> 4)
+
 static inline void iphc_uncompress_eui64_lladdr(struct in6_addr *ipaddr,
                                                const void *lladdr)
 {
@@ -195,6 +199,98 @@ static inline void iphc_uncompress_802154_lladdr(struct in6_addr *ipaddr,
        }
 }
 
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_id(const struct net_device *dev, u8 id)
+{
+       struct lowpan_iphc_ctx *ret = &lowpan_priv(dev)->ctx.table[id];
+
+       if (!lowpan_iphc_ctx_is_active(ret))
+               return NULL;
+
+       return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_addr(const struct net_device *dev,
+                           const struct in6_addr *addr)
+{
+       struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
+       struct lowpan_iphc_ctx *ret = NULL;
+       struct in6_addr addr_pfx;
+       u8 addr_plen;
+       int i;
+
+       for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+               /* Check if context is valid. A context that is not valid
+                * MUST NOT be used for compression.
+                */
+               if (!lowpan_iphc_ctx_is_active(&table[i]) ||
+                   !lowpan_iphc_ctx_is_compression(&table[i]))
+                       continue;
+
+               ipv6_addr_prefix(&addr_pfx, addr, table[i].plen);
+
+               /* if prefix len < 64, the remaining bits until 64th bit is
+                * zero. Otherwise we use table[i]->plen.
+                */
+               if (table[i].plen < 64)
+                       addr_plen = 64;
+               else
+                       addr_plen = table[i].plen;
+
+               if (ipv6_prefix_equal(&addr_pfx, &table[i].pfx, addr_plen)) {
+                       /* remember first match */
+                       if (!ret) {
+                               ret = &table[i];
+                               continue;
+                       }
+
+                       /* get the context with longest prefix len */
+                       if (table[i].plen > ret->plen)
+                               ret = &table[i];
+               }
+       }
+
+       return ret;
+}
+
+static struct lowpan_iphc_ctx *
+lowpan_iphc_ctx_get_by_mcast_addr(const struct net_device *dev,
+                                 const struct in6_addr *addr)
+{
+       struct lowpan_iphc_ctx *table = lowpan_priv(dev)->ctx.table;
+       struct lowpan_iphc_ctx *ret = NULL;
+       struct in6_addr addr_mcast, network_pfx = {};
+       int i;
+
+       /* init mcast address with  */
+       memcpy(&addr_mcast, addr, sizeof(*addr));
+
+       for (i = 0; i < LOWPAN_IPHC_CTX_TABLE_SIZE; i++) {
+               /* Check if context is valid. A context that is not valid
+                * MUST NOT be used for compression.
+                */
+               if (!lowpan_iphc_ctx_is_active(&table[i]) ||
+                   !lowpan_iphc_ctx_is_compression(&table[i]))
+                       continue;
+
+               /* setting plen */
+               addr_mcast.s6_addr[3] = table[i].plen;
+               /* get network prefix to copy into multicast address */
+               ipv6_addr_prefix(&network_pfx, &table[i].pfx,
+                                table[i].plen);
+               /* setting network prefix */
+               memcpy(&addr_mcast.s6_addr[4], &network_pfx, 8);
+
+               if (ipv6_addr_equal(addr, &addr_mcast)) {
+                       ret = &table[i];
+                       break;
+               }
+       }
+
+       return ret;
+}
+
 /* Uncompress address function for source and
  * destination address(non-multicast).
  *
@@ -259,30 +355,59 @@ static int uncompress_addr(struct sk_buff *skb, const struct net_device *dev,
 /* Uncompress address function for source context
  * based address(non-multicast).
  */
-static int uncompress_context_based_src_addr(struct sk_buff *skb,
-                                            struct in6_addr *ipaddr,
-                                            u8 address_mode)
+static int uncompress_ctx_addr(struct sk_buff *skb,
+                              const struct net_device *dev,
+                              const struct lowpan_iphc_ctx *ctx,
+                              struct in6_addr *ipaddr, u8 address_mode,
+                              const void *lladdr)
 {
+       bool fail;
+
        switch (address_mode) {
-       case LOWPAN_IPHC_SAM_00:
-               /* unspec address ::
+       /* SAM and DAM are the same here */
+       case LOWPAN_IPHC_DAM_00:
+               fail = false;
+               /* SAM_00 -> unspec address ::
                 * Do nothing, address is already ::
+                *
+                * DAM 00 -> reserved should never occur.
                 */
                break;
        case LOWPAN_IPHC_SAM_01:
-               /* TODO */
+       case LOWPAN_IPHC_DAM_01:
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[8], 8);
+               ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+               break;
        case LOWPAN_IPHC_SAM_10:
-               /* TODO */
+       case LOWPAN_IPHC_DAM_10:
+               ipaddr->s6_addr[11] = 0xFF;
+               ipaddr->s6_addr[12] = 0xFE;
+               fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[14], 2);
+               ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+               break;
        case LOWPAN_IPHC_SAM_11:
-               /* TODO */
-               netdev_warn(skb->dev, "SAM value 0x%x not supported\n",
-                           address_mode);
-               return -EINVAL;
+       case LOWPAN_IPHC_DAM_11:
+               fail = false;
+               switch (lowpan_priv(dev)->lltype) {
+               case LOWPAN_LLTYPE_IEEE802154:
+                       iphc_uncompress_802154_lladdr(ipaddr, lladdr);
+                       break;
+               default:
+                       iphc_uncompress_eui64_lladdr(ipaddr, lladdr);
+                       break;
+               }
+               ipv6_addr_prefix_copy(ipaddr, &ctx->pfx, ctx->plen);
+               break;
        default:
                pr_debug("Invalid sam value: 0x%x\n", address_mode);
                return -EINVAL;
        }
 
+       if (fail) {
+               pr_debug("Failed to fetch skb data\n");
+               return -EIO;
+       }
+
        raw_dump_inline(NULL,
                        "Reconstructed context based ipv6 src addr is",
                        ipaddr->s6_addr, 16);
@@ -346,6 +471,30 @@ static int lowpan_uncompress_multicast_daddr(struct sk_buff *skb,
        return 0;
 }
 
+static int lowpan_uncompress_multicast_ctx_daddr(struct sk_buff *skb,
+                                                struct lowpan_iphc_ctx *ctx,
+                                                struct in6_addr *ipaddr,
+                                                u8 address_mode)
+{
+       struct in6_addr network_pfx = {};
+       bool fail;
+
+       ipaddr->s6_addr[0] = 0xFF;
+       fail = lowpan_fetch_skb(skb, &ipaddr->s6_addr[1], 2);
+       fail |= lowpan_fetch_skb(skb, &ipaddr->s6_addr[12], 4);
+       if (fail)
+               return -EIO;
+
+       /* take prefix_len and network prefix from the context */
+       ipaddr->s6_addr[3] = ctx->plen;
+       /* get network prefix to copy into multicast address */
+       ipv6_addr_prefix(&network_pfx, &ctx->pfx, ctx->plen);
+       /* setting network prefix */
+       memcpy(&ipaddr->s6_addr[4], &network_pfx, 8);
+
+       return 0;
+}
+
 /* get the ecn values from iphc tf format and set it to ipv6hdr */
 static inline void lowpan_iphc_tf_set_ecn(struct ipv6hdr *hdr, const u8 *tf)
 {
@@ -459,7 +608,8 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
                             const void *daddr, const void *saddr)
 {
        struct ipv6hdr hdr = {};
-       u8 iphc0, iphc1;
+       struct lowpan_iphc_ctx *ci;
+       u8 iphc0, iphc1, cid = 0;
        int err;
 
        raw_dump_table(__func__, "raw skb data dump uncompressed",
@@ -469,12 +619,14 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
            lowpan_fetch_skb(skb, &iphc1, sizeof(iphc1)))
                return -EINVAL;
 
-       /* another if the CID flag is set */
-       if (iphc1 & LOWPAN_IPHC_CID)
-               return -ENOTSUPP;
-
        hdr.version = 6;
 
+       /* default CID = 0, another if the CID flag is set */
+       if (iphc1 & LOWPAN_IPHC_CID) {
+               if (lowpan_fetch_skb(skb, &cid, sizeof(cid)))
+                       return -EINVAL;
+       }
+
        err = lowpan_iphc_tf_decompress(skb, &hdr,
                                        iphc0 & LOWPAN_IPHC_TF_MASK);
        if (err < 0)
@@ -500,10 +652,17 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
        }
 
        if (iphc1 & LOWPAN_IPHC_SAC) {
-               /* Source address context based uncompression */
+               spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+               ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_SCI(cid));
+               if (!ci) {
+                       spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+                       return -EINVAL;
+               }
+
                pr_debug("SAC bit is set. Handle context based source address.\n");
-               err = uncompress_context_based_src_addr(skb, &hdr.saddr,
-                                                       iphc1 & LOWPAN_IPHC_SAM_MASK);
+               err = uncompress_ctx_addr(skb, dev, ci, &hdr.saddr,
+                                         iphc1 & LOWPAN_IPHC_SAM_MASK, saddr);
+               spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
        } else {
                /* Source address uncompression */
                pr_debug("source address stateless compression\n");
@@ -515,27 +674,52 @@ int lowpan_header_decompress(struct sk_buff *skb, const struct net_device *dev,
        if (err)
                return -EINVAL;
 
-       /* check for Multicast Compression */
-       if (iphc1 & LOWPAN_IPHC_M) {
-               if (iphc1 & LOWPAN_IPHC_DAC) {
-                       pr_debug("dest: context-based mcast compression\n");
-                       /* TODO: implement this */
-               } else {
-                       err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
-                                                               iphc1 & LOWPAN_IPHC_DAM_MASK);
+       switch (iphc1 & (LOWPAN_IPHC_M | LOWPAN_IPHC_DAC)) {
+       case LOWPAN_IPHC_M | LOWPAN_IPHC_DAC:
+               spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+               ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
+               if (!ci) {
+                       spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+                       return -EINVAL;
+               }
 
-                       if (err)
-                               return -EINVAL;
+               /* multicast with context */
+               pr_debug("dest: context-based mcast compression\n");
+               err = lowpan_uncompress_multicast_ctx_daddr(skb, ci,
+                                                           &hdr.daddr,
+                                                           iphc1 & LOWPAN_IPHC_DAM_MASK);
+               spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+               break;
+       case LOWPAN_IPHC_M:
+               /* multicast */
+               err = lowpan_uncompress_multicast_daddr(skb, &hdr.daddr,
+                                                       iphc1 & LOWPAN_IPHC_DAM_MASK);
+               break;
+       case LOWPAN_IPHC_DAC:
+               spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+               ci = lowpan_iphc_ctx_get_by_id(dev, LOWPAN_IPHC_CID_DCI(cid));
+               if (!ci) {
+                       spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+                       return -EINVAL;
                }
-       } else {
+
+               /* Destination address context based uncompression */
+               pr_debug("DAC bit is set. Handle context based destination address.\n");
+               err = uncompress_ctx_addr(skb, dev, ci, &hdr.daddr,
+                                         iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
+               spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+               break;
+       default:
                err = uncompress_addr(skb, dev, &hdr.daddr,
                                      iphc1 & LOWPAN_IPHC_DAM_MASK, daddr);
                pr_debug("dest: stateless compression mode %d dest %pI6c\n",
                         iphc1 & LOWPAN_IPHC_DAM_MASK, &hdr.daddr);
-               if (err)
-                       return -EINVAL;
+               break;
        }
 
+       if (err)
+               return -EINVAL;
+
        /* Next header data uncompression */
        if (iphc0 & LOWPAN_IPHC_NH) {
                err = lowpan_nhc_do_uncompression(skb, dev, &hdr);
@@ -585,6 +769,58 @@ static const u8 lowpan_iphc_dam_to_sam_value[] = {
        [LOWPAN_IPHC_DAM_11] = LOWPAN_IPHC_SAM_11,
 };
 
+static u8 lowpan_compress_ctx_addr(u8 **hc_ptr, const struct in6_addr *ipaddr,
+                                  const struct lowpan_iphc_ctx *ctx,
+                                  const unsigned char *lladdr, bool sam)
+{
+       struct in6_addr tmp = {};
+       u8 dam;
+
+       /* check for SAM/DAM = 11 */
+       memcpy(&tmp.s6_addr[8], lladdr, 8);
+       /* second bit-flip (Universe/Local) is done according RFC2464 */
+       tmp.s6_addr[8] ^= 0x02;
+       /* context information are always used */
+       ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+       if (ipv6_addr_equal(&tmp, ipaddr)) {
+               dam = LOWPAN_IPHC_DAM_11;
+               goto out;
+       }
+
+       memset(&tmp, 0, sizeof(tmp));
+       /* check for SAM/DAM = 01 */
+       tmp.s6_addr[11] = 0xFF;
+       tmp.s6_addr[12] = 0xFE;
+       memcpy(&tmp.s6_addr[14], &ipaddr->s6_addr[14], 2);
+       /* context information are always used */
+       ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+       if (ipv6_addr_equal(&tmp, ipaddr)) {
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[14], 2);
+               dam = LOWPAN_IPHC_DAM_10;
+               goto out;
+       }
+
+       memset(&tmp, 0, sizeof(tmp));
+       /* check for SAM/DAM = 10, should always match */
+       memcpy(&tmp.s6_addr[8], &ipaddr->s6_addr[8], 8);
+       /* context information are always used */
+       ipv6_addr_prefix_copy(&tmp, &ctx->pfx, ctx->plen);
+       if (ipv6_addr_equal(&tmp, ipaddr)) {
+               lowpan_push_hc_data(hc_ptr, &ipaddr->s6_addr[8], 8);
+               dam = LOWPAN_IPHC_DAM_01;
+               goto out;
+       }
+
+       WARN_ONCE(1, "context found but no address mode matched\n");
+       return LOWPAN_IPHC_DAM_00;
+out:
+
+       if (sam)
+               return lowpan_iphc_dam_to_sam_value[dam];
+       else
+               return dam;
+}
+
 static u8 lowpan_compress_addr_64(u8 **hc_ptr, const struct in6_addr *ipaddr,
                                  const unsigned char *lladdr, bool sam)
 {
@@ -708,6 +944,21 @@ static u8 lowpan_iphc_tf_compress(u8 **hc_ptr, const struct ipv6hdr *hdr)
        return val;
 }
 
+static u8 lowpan_iphc_mcast_ctx_addr_compress(u8 **hc_ptr,
+                                             const struct lowpan_iphc_ctx *ctx,
+                                             const struct in6_addr *ipaddr)
+{
+       u8 data[6];
+
+       /* flags/scope, reserved (RIID) */
+       memcpy(data, &ipaddr->s6_addr[1], 2);
+       /* group ID */
+       memcpy(&data[1], &ipaddr->s6_addr[11], 4);
+       lowpan_push_hc_data(hc_ptr, data, 6);
+
+       return LOWPAN_IPHC_DAM_00;
+}
+
 static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
                                          const struct in6_addr *ipaddr)
 {
@@ -742,10 +993,11 @@ static u8 lowpan_iphc_mcast_addr_compress(u8 **hc_ptr,
 int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
                           const void *daddr, const void *saddr)
 {
-       u8 iphc0, iphc1, *hc_ptr;
+       u8 iphc0, iphc1, *hc_ptr, cid = 0;
        struct ipv6hdr *hdr;
        u8 head[LOWPAN_IPHC_MAX_HC_BUF_LEN] = {};
-       int ret, addr_type;
+       struct lowpan_iphc_ctx *dci, *sci, dci_entry, sci_entry;
+       int ret, ipv6_daddr_type, ipv6_saddr_type;
 
        if (skb->protocol != htons(ETH_P_IPV6))
                return -EINVAL;
@@ -769,14 +1021,38 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
        iphc0 = LOWPAN_DISPATCH_IPHC;
        iphc1 = 0;
 
-       /* TODO: context lookup */
-
        raw_dump_inline(__func__, "saddr", saddr, EUI64_ADDR_LEN);
        raw_dump_inline(__func__, "daddr", daddr, EUI64_ADDR_LEN);
 
        raw_dump_table(__func__, "sending raw skb network uncompressed packet",
                       skb->data, skb->len);
 
+       ipv6_daddr_type = ipv6_addr_type(&hdr->daddr);
+       spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+       if (ipv6_daddr_type & IPV6_ADDR_MULTICAST)
+               dci = lowpan_iphc_ctx_get_by_mcast_addr(dev, &hdr->daddr);
+       else
+               dci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->daddr);
+       if (dci) {
+               memcpy(&dci_entry, dci, sizeof(*dci));
+               cid |= dci->id;
+       }
+       spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+
+       spin_lock_bh(&lowpan_priv(dev)->ctx.lock);
+       sci = lowpan_iphc_ctx_get_by_addr(dev, &hdr->saddr);
+       if (sci) {
+               memcpy(&sci_entry, sci, sizeof(*sci));
+               cid |= (sci->id << 4);
+       }
+       spin_unlock_bh(&lowpan_priv(dev)->ctx.lock);
+
+       /* if cid is zero it will be compressed */
+       if (cid) {
+               iphc1 |= LOWPAN_IPHC_CID;
+               lowpan_push_hc_data(&hc_ptr, &cid, sizeof(cid));
+       }
+
        /* Traffic Class, Flow Label compression */
        iphc0 |= lowpan_iphc_tf_compress(&hc_ptr, hdr);
 
@@ -813,39 +1089,64 @@ int lowpan_header_compress(struct sk_buff *skb, const struct net_device *dev,
                                    sizeof(hdr->hop_limit));
        }
 
-       addr_type = ipv6_addr_type(&hdr->saddr);
+       ipv6_saddr_type = ipv6_addr_type(&hdr->saddr);
        /* source address compression */
-       if (addr_type == IPV6_ADDR_ANY) {
+       if (ipv6_saddr_type == IPV6_ADDR_ANY) {
                pr_debug("source address is unspecified, setting SAC\n");
                iphc1 |= LOWPAN_IPHC_SAC;
        } else {
-               if (addr_type & IPV6_ADDR_LINKLOCAL) {
-                       iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->saddr,
-                                                        saddr, true);
-                       pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
-                                &hdr->saddr, iphc1);
+               if (sci) {
+                       iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->saddr,
+                                                         &sci_entry, saddr,
+                                                         true);
+                       iphc1 |= LOWPAN_IPHC_SAC;
                } else {
-                       pr_debug("send the full source address\n");
-                       lowpan_push_hc_data(&hc_ptr, hdr->saddr.s6_addr, 16);
+                       if (ipv6_saddr_type & IPV6_ADDR_LINKLOCAL) {
+                               iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+                                                                &hdr->saddr,
+                                                                saddr, true);
+                               pr_debug("source address unicast link-local %pI6c iphc1 0x%02x\n",
+                                        &hdr->saddr, iphc1);
+                       } else {
+                               pr_debug("send the full source address\n");
+                               lowpan_push_hc_data(&hc_ptr,
+                                                   hdr->saddr.s6_addr, 16);
+                       }
                }
        }
 
-       addr_type = ipv6_addr_type(&hdr->daddr);
        /* destination address compression */
-       if (addr_type & IPV6_ADDR_MULTICAST) {
+       if (ipv6_daddr_type & IPV6_ADDR_MULTICAST) {
                pr_debug("destination address is multicast: ");
                iphc1 |= LOWPAN_IPHC_M;
-               iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr, &hdr->daddr);
+               if (dci) {
+                       iphc1 |= lowpan_iphc_mcast_ctx_addr_compress(&hc_ptr,
+                                                                    &dci_entry,
+                                                                    &hdr->daddr);
+                       iphc1 |= LOWPAN_IPHC_DAC;
+               } else {
+                       iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
+                                                                &hdr->daddr);
+               }
        } else {
-               if (addr_type & IPV6_ADDR_LINKLOCAL) {
-                       /* TODO: context lookup */
-                       iphc1 |= lowpan_compress_addr_64(&hc_ptr, &hdr->daddr,
-                                                        daddr, false);
-                       pr_debug("dest address unicast link-local %pI6c "
-                                "iphc1 0x%02x\n", &hdr->daddr, iphc1);
+               if (dci) {
+                       iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
+                                                         &dci_entry, daddr,
+                                                         false);
+                       iphc1 |= LOWPAN_IPHC_DAC;
                } else {
-                       pr_debug("dest address unicast %pI6c\n", &hdr->daddr);
-                       lowpan_push_hc_data(&hc_ptr, hdr->daddr.s6_addr, 16);
+                       if (ipv6_daddr_type & IPV6_ADDR_LINKLOCAL) {
+                               iphc1 |= lowpan_compress_addr_64(&hc_ptr,
+                                                                &hdr->daddr,
+                                                                daddr, false);
+                               pr_debug("dest address unicast link-local %pI6c iphc1 0x%02x\n",
+                                        &hdr->daddr, iphc1);
+                       } else {
+                               pr_debug("dest address unicast %pI6c\n",
+                                        &hdr->daddr);
+                               lowpan_push_hc_data(&hc_ptr,
+                                                   hdr->daddr.s6_addr, 16);
+                       }
                }
        }
 
index 95d1a66ba03aa20095932a1c45f9f76af2cc1393..06c31b9a68b0bce3cc4f28224c3c23864f78e7c9 100644 (file)
@@ -69,6 +69,15 @@ config BT_6LOWPAN
        help
          IPv6 compression over Bluetooth Low Energy.
 
+config BT_LEDS
+       bool "Enable LED triggers"
+       depends on BT
+       depends on LEDS_CLASS
+       select LEDS_TRIGGERS
+       help
+         This option selects a few LED triggers for different
+         Bluetooth events.
+
 config BT_SELFTEST
        bool "Bluetooth self testing support"
        depends on BT && DEBUG_KERNEL
index 2b15ae8c1def06642682c488a9439f0e57feeb13..b3ff12eb9b6dcdaa590bacc997cbe2e0e566783e 100644 (file)
@@ -17,6 +17,7 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
 
 bluetooth-$(CONFIG_BT_BREDR) += sco.o
 bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
+bluetooth-$(CONFIG_BT_LEDS) += leds.o
 bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
 bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
 
index 883c821a9e784851a89c00e3874b7d9da9628a69..2713fc86e85abd780d9bccfb2bb11cb353d4fcb9 100644 (file)
@@ -40,6 +40,7 @@
 #include "hci_request.h"
 #include "hci_debugfs.h"
 #include "smp.h"
+#include "leds.h"
 
 static void hci_rx_work(struct work_struct *work);
 static void hci_cmd_work(struct work_struct *work);
@@ -1395,6 +1396,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
                hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
                set_bit(HCI_UP, &hdev->flags);
                hci_sock_dev_event(hdev, HCI_DEV_UP);
+               hci_leds_update_powered(hdev, true);
                if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
                    !hci_dev_test_flag(hdev, HCI_CONFIG) &&
                    !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
@@ -1532,6 +1534,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
                return 0;
        }
 
+       hci_leds_update_powered(hdev, false);
+
        /* Flush RX and TX works */
        flush_work(&hdev->tx_work);
        flush_work(&hdev->rx_work);
@@ -2017,6 +2021,7 @@ static void hci_power_on(struct work_struct *work)
        if (test_bit(HCI_UP, &hdev->flags) &&
            hci_dev_test_flag(hdev, HCI_MGMT) &&
            hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
+               cancel_delayed_work(&hdev->power_off);
                hci_req_sync_lock(hdev);
                err = __hci_req_hci_power_on(hdev);
                hci_req_sync_unlock(hdev);
@@ -3067,6 +3072,8 @@ int hci_register_dev(struct hci_dev *hdev)
        if (error < 0)
                goto err_wqueue;
 
+       hci_leds_init(hdev);
+
        hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
                                    RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops,
                                    hdev);
diff --git a/net/bluetooth/leds.c b/net/bluetooth/leds.c
new file mode 100644 (file)
index 0000000..8319c84
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015, Heiner Kallweit <hkallweit1@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "leds.h"
+
+struct hci_basic_led_trigger {
+       struct led_trigger      led_trigger;
+       struct hci_dev          *hdev;
+};
+
+#define to_hci_basic_led_trigger(arg) container_of(arg, \
+                       struct hci_basic_led_trigger, led_trigger)
+
+void hci_leds_update_powered(struct hci_dev *hdev, bool enabled)
+{
+       if (hdev->power_led)
+               led_trigger_event(hdev->power_led,
+                                 enabled ? LED_FULL : LED_OFF);
+}
+
+static void power_activate(struct led_classdev *led_cdev)
+{
+       struct hci_basic_led_trigger *htrig;
+       bool powered;
+
+       htrig = to_hci_basic_led_trigger(led_cdev->trigger);
+       powered = test_bit(HCI_UP, &htrig->hdev->flags);
+
+       led_trigger_event(led_cdev->trigger, powered ? LED_FULL : LED_OFF);
+}
+
+static struct led_trigger *led_allocate_basic(struct hci_dev *hdev,
+                       void (*activate)(struct led_classdev *led_cdev),
+                       const char *name)
+{
+       struct hci_basic_led_trigger *htrig;
+
+       htrig = devm_kzalloc(&hdev->dev, sizeof(*htrig), GFP_KERNEL);
+       if (!htrig)
+               return NULL;
+
+       htrig->hdev = hdev;
+       htrig->led_trigger.activate = activate;
+       htrig->led_trigger.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
+                                                "%s-%s", hdev->name,
+                                                name);
+       if (!htrig->led_trigger.name)
+               goto err_alloc;
+
+       if (devm_led_trigger_register(&hdev->dev, &htrig->led_trigger))
+               goto err_register;
+
+       return &htrig->led_trigger;
+
+err_register:
+       devm_kfree(&hdev->dev, (void *)htrig->led_trigger.name);
+err_alloc:
+       devm_kfree(&hdev->dev, htrig);
+       return NULL;
+}
+
+void hci_leds_init(struct hci_dev *hdev)
+{
+       /* initialize power_led */
+       hdev->power_led = led_allocate_basic(hdev, power_activate, "power");
+}
diff --git a/net/bluetooth/leds.h b/net/bluetooth/leds.h
new file mode 100644 (file)
index 0000000..a9c4d6e
--- /dev/null
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2015, Heiner Kallweit <hkallweit1@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#if IS_ENABLED(CONFIG_BT_LEDS)
+void hci_leds_update_powered(struct hci_dev *hdev, bool enabled);
+void hci_leds_init(struct hci_dev *hdev);
+#else
+static inline void hci_leds_update_powered(struct hci_dev *hdev,
+                                          bool enabled) {}
+static inline void hci_leds_init(struct hci_dev *hdev) {}
+#endif
index 737c87a2a41e0299aecff8d56c30b637c6baa02e..0023c904881246ab00432c1bcb3454df1c79636a 100644 (file)
@@ -207,7 +207,7 @@ static int lowpan_device_event(struct notifier_block *unused,
        struct net_device *wdev = netdev_notifier_info_to_dev(ptr);
 
        if (wdev->type != ARPHRD_IEEE802154)
-               goto out;
+               return NOTIFY_DONE;
 
        switch (event) {
        case NETDEV_UNREGISTER:
@@ -219,11 +219,10 @@ static int lowpan_device_event(struct notifier_block *unused,
                        lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
                break;
        default:
-               break;
+               return NOTIFY_DONE;
        }
 
-out:
-       return NOTIFY_DONE;
+       return NOTIFY_OK;
 }
 
 static struct notifier_block lowpan_dev_notifier = {
index e8cab5bb80c664669f9c34b36e5e9178db83ae13..87da85ae5a6b1ace5f6cdda2c0a9b8e01c8c5ba6 100644 (file)
@@ -218,7 +218,6 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
 
        tasklet_kill(&local->tasklet);
        flush_workqueue(local->workqueue);
-       destroy_workqueue(local->workqueue);
 
        rtnl_lock();
 
@@ -226,6 +225,7 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
 
        rtnl_unlock();
 
+       destroy_workqueue(local->workqueue);
        wpan_phy_unregister(local->phy);
 }
 EXPORT_SYMBOL(ieee802154_unregister_hw);
This page took 0.057631 seconds and 5 git commands to generate.