Commit aefd3fb2 authored by David S. Miller's avatar David S. Miller

Merge branch 'for-upstream' of...

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

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 5eb4dce3 34bf1912
...@@ -151,7 +151,7 @@ S: Maintained ...@@ -151,7 +151,7 @@ S: Maintained
F: drivers/scsi/53c700* F: drivers/scsi/53c700*
6LOWPAN GENERIC (BTLE/IEEE 802.15.4) 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> M: Jukka Rissanen <jukka.rissanen@linux.intel.com>
L: linux-bluetooth@vger.kernel.org L: linux-bluetooth@vger.kernel.org
L: linux-wpan@vger.kernel.org L: linux-wpan@vger.kernel.org
...@@ -5424,10 +5424,11 @@ S: Supported ...@@ -5424,10 +5424,11 @@ S: Supported
F: drivers/idle/i7300_idle.c F: drivers/idle/i7300_idle.c
IEEE 802.15.4 SUBSYSTEM IEEE 802.15.4 SUBSYSTEM
M: Alexander Aring <alex.aring@gmail.com> M: Alexander Aring <aar@pengutronix.de>
L: linux-wpan@vger.kernel.org L: linux-wpan@vger.kernel.org
W: https://github.com/linux-wpan W: http://wpan.cakelab.org/
T: git git://github.com/linux-wpan/linux-wpan-next.git 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 S: Maintained
F: net/ieee802154/ F: net/ieee802154/
F: net/mac802154/ F: net/mac802154/
......
...@@ -169,6 +169,17 @@ config BT_HCIUART_QCA ...@@ -169,6 +169,17 @@ config BT_HCIUART_QCA
Say Y here to compile support for QCA protocol. 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 config BT_HCIBCM203X
tristate "HCI BCM203x USB driver" tristate "HCI BCM203x USB driver"
depends on USB depends on USB
......
...@@ -36,6 +36,7 @@ hci_uart-$(CONFIG_BT_HCIUART_3WIRE) += hci_h5.o ...@@ -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_INTEL) += hci_intel.o
hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o hci_uart-$(CONFIG_BT_HCIUART_BCM) += hci_bcm.o
hci_uart-$(CONFIG_BT_HCIUART_QCA) += hci_qca.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) hci_uart-objs := $(hci_uart-y)
ccflags-y += -D__CHECK_ENDIAN__ ccflags-y += -D__CHECK_ENDIAN__
...@@ -82,6 +82,7 @@ static const struct usb_device_id ath3k_table[] = { ...@@ -82,6 +82,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x0489, 0xe05f) }, { USB_DEVICE(0x0489, 0xe05f) },
{ USB_DEVICE(0x0489, 0xe076) }, { USB_DEVICE(0x0489, 0xe076) },
{ USB_DEVICE(0x0489, 0xe078) }, { USB_DEVICE(0x0489, 0xe078) },
{ USB_DEVICE(0x0489, 0xe095) },
{ USB_DEVICE(0x04c5, 0x1330) }, { USB_DEVICE(0x04c5, 0x1330) },
{ USB_DEVICE(0x04CA, 0x3004) }, { USB_DEVICE(0x04CA, 0x3004) },
{ USB_DEVICE(0x04CA, 0x3005) }, { USB_DEVICE(0x04CA, 0x3005) },
...@@ -92,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = { ...@@ -92,6 +93,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x04CA, 0x300d) }, { USB_DEVICE(0x04CA, 0x300d) },
{ USB_DEVICE(0x04CA, 0x300f) }, { USB_DEVICE(0x04CA, 0x300f) },
{ USB_DEVICE(0x04CA, 0x3010) }, { USB_DEVICE(0x04CA, 0x3010) },
{ USB_DEVICE(0x04CA, 0x3014) },
{ USB_DEVICE(0x0930, 0x0219) }, { USB_DEVICE(0x0930, 0x0219) },
{ USB_DEVICE(0x0930, 0x021c) }, { USB_DEVICE(0x0930, 0x021c) },
{ USB_DEVICE(0x0930, 0x0220) }, { USB_DEVICE(0x0930, 0x0220) },
...@@ -113,6 +115,7 @@ static const struct usb_device_id ath3k_table[] = { ...@@ -113,6 +115,7 @@ static const struct usb_device_id ath3k_table[] = {
{ USB_DEVICE(0x13d3, 0x3362) }, { USB_DEVICE(0x13d3, 0x3362) },
{ USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x13d3, 0x3375) },
{ USB_DEVICE(0x13d3, 0x3393) }, { USB_DEVICE(0x13d3, 0x3393) },
{ USB_DEVICE(0x13d3, 0x3395) },
{ USB_DEVICE(0x13d3, 0x3402) }, { USB_DEVICE(0x13d3, 0x3402) },
{ USB_DEVICE(0x13d3, 0x3408) }, { USB_DEVICE(0x13d3, 0x3408) },
{ USB_DEVICE(0x13d3, 0x3423) }, { USB_DEVICE(0x13d3, 0x3423) },
...@@ -144,6 +147,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { ...@@ -144,6 +147,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .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(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .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[] = { ...@@ -154,6 +158,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .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, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .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[] = { ...@@ -175,6 +180,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .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, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
...@@ -497,6 +503,7 @@ static int ath3k_probe(struct usb_interface *intf, ...@@ -497,6 +503,7 @@ static int ath3k_probe(struct usb_interface *intf,
/* match device ID in ath3k blacklist table */ /* match device ID in ath3k blacklist table */
if (!id->driver_info) { if (!id->driver_info) {
const struct usb_device_id *match; const struct usb_device_id *match;
match = usb_match_id(intf, ath3k_blist_tbl); match = usb_match_id(intf, ath3k_blist_tbl);
if (match) if (match)
id = match; id = match;
......
...@@ -467,7 +467,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev) ...@@ -467,7 +467,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
err = request_firmware(&fw, fw_name, &hdev->dev); err = request_firmware(&fw, fw_name, &hdev->dev);
if (err < 0) { if (err < 0) {
BT_INFO("%s: BCM: Patch %s not found", hdev->name, fw_name); BT_INFO("%s: BCM: Patch %s not found", hdev->name, fw_name);
return 0; goto done;
} }
btbcm_patchram(hdev, fw); btbcm_patchram(hdev, fw);
...@@ -501,6 +501,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev) ...@@ -501,6 +501,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1)); BT_INFO("%s: %s", hdev->name, (char *)(skb->data + 1));
kfree_skb(skb); kfree_skb(skb);
done:
btbcm_check_bdaddr(hdev); btbcm_check_bdaddr(hdev);
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
......
...@@ -196,6 +196,7 @@ static const struct usb_device_id blacklist_table[] = { ...@@ -196,6 +196,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe05f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0489, 0xe076), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0489, 0xe078), .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(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x3005), .driver_info = BTUSB_ATH3012 },
...@@ -206,6 +207,7 @@ static const struct usb_device_id blacklist_table[] = { ...@@ -206,6 +207,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300d), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x04ca, 0x300f), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x04ca, 0x3010), .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, 0x0219), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x021c), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0930, 0x0220), .driver_info = BTUSB_ATH3012 },
...@@ -227,6 +229,7 @@ static const struct usb_device_id blacklist_table[] = { ...@@ -227,6 +229,7 @@ static const struct usb_device_id blacklist_table[] = {
{ USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3362), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3393), .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, 0x3402), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
......
/*
*
* 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);
}
...@@ -820,10 +820,12 @@ static const struct acpi_device_id bcm_acpi_match[] = { ...@@ -820,10 +820,12 @@ static const struct acpi_device_id bcm_acpi_match[] = {
{ "BCM2E3D", 0 }, { "BCM2E3D", 0 },
{ "BCM2E3F", 0 }, { "BCM2E3F", 0 },
{ "BCM2E40", 0 }, { "BCM2E40", 0 },
{ "BCM2E54", 0 },
{ "BCM2E64", 0 }, { "BCM2E64", 0 },
{ "BCM2E65", 0 }, { "BCM2E65", 0 },
{ "BCM2E67", 0 }, { "BCM2E67", 0 },
{ "BCM2E7B", 0 }, { "BCM2E7B", 0 },
{ "BCM2E7C", 0 },
{ }, { },
}; };
MODULE_DEVICE_TABLE(acpi, bcm_acpi_match); MODULE_DEVICE_TABLE(acpi, bcm_acpi_match);
......
...@@ -488,7 +488,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed) ...@@ -488,7 +488,7 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
clear_bit(STATE_BOOTING, &intel->flags); clear_bit(STATE_BOOTING, &intel->flags);
/* In case of timeout, try to continue anyway */ /* In case of timeout, try to continue anyway */
if (err && err != ETIMEDOUT) if (err && err != -ETIMEDOUT)
return err; return err;
bt_dev_info(hdev, "Change controller speed to %d", speed); bt_dev_info(hdev, "Change controller speed to %d", speed);
...@@ -581,7 +581,7 @@ static int intel_setup(struct hci_uart *hu) ...@@ -581,7 +581,7 @@ static int intel_setup(struct hci_uart *hu)
clear_bit(STATE_BOOTING, &intel->flags); clear_bit(STATE_BOOTING, &intel->flags);
/* In case of timeout, try to continue anyway */ /* In case of timeout, try to continue anyway */
if (err && err != ETIMEDOUT) if (err && err != -ETIMEDOUT)
return err; return err;
set_bit(STATE_BOOTLOADER, &intel->flags); set_bit(STATE_BOOTLOADER, &intel->flags);
......
...@@ -804,6 +804,9 @@ static int __init hci_uart_init(void) ...@@ -804,6 +804,9 @@ static int __init hci_uart_init(void)
#ifdef CONFIG_BT_HCIUART_QCA #ifdef CONFIG_BT_HCIUART_QCA
qca_init(); qca_init();
#endif #endif
#ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_init();
#endif
return 0; return 0;
} }
...@@ -836,6 +839,9 @@ static void __exit hci_uart_exit(void) ...@@ -836,6 +839,9 @@ static void __exit hci_uart_exit(void)
#ifdef CONFIG_BT_HCIUART_QCA #ifdef CONFIG_BT_HCIUART_QCA
qca_deinit(); qca_deinit();
#endif #endif
#ifdef CONFIG_BT_HCIUART_AG6XX
ag6xx_deinit();
#endif
/* Release tty registration of line discipline */ /* Release tty registration of line discipline */
err = tty_unregister_ldisc(N_HCI); err = tty_unregister_ldisc(N_HCI);
......
...@@ -35,7 +35,7 @@ ...@@ -35,7 +35,7 @@
#define HCIUARTGETFLAGS _IOR('U', 204, int) #define HCIUARTGETFLAGS _IOR('U', 204, int)
/* UART protocols */ /* UART protocols */
#define HCI_UART_MAX_PROTO 9 #define HCI_UART_MAX_PROTO 10
#define HCI_UART_H4 0 #define HCI_UART_H4 0
#define HCI_UART_BCSP 1 #define HCI_UART_BCSP 1
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#define HCI_UART_INTEL 6 #define HCI_UART_INTEL 6
#define HCI_UART_BCM 7 #define HCI_UART_BCM 7
#define HCI_UART_QCA 8 #define HCI_UART_QCA 8
#define HCI_UART_AG6XX 9
#define HCI_UART_RAW_DEVICE 0 #define HCI_UART_RAW_DEVICE 0
#define HCI_UART_RESET_ON_INIT 1 #define HCI_UART_RESET_ON_INIT 1
...@@ -182,3 +183,8 @@ int bcm_deinit(void); ...@@ -182,3 +183,8 @@ int bcm_deinit(void);
int qca_init(void); int qca_init(void);
int qca_deinit(void); int qca_deinit(void);
#endif #endif
#ifdef CONFIG_BT_HCIUART_AG6XX
int ag6xx_init(void);
int ag6xx_deinit(void);
#endif
...@@ -343,16 +343,26 @@ static const struct regmap_config at86rf230_regmap_spi_config = { ...@@ -343,16 +343,26 @@ static const struct regmap_config at86rf230_regmap_spi_config = {
}; };
static void static void
at86rf230_async_error_recover(void *context) at86rf230_async_error_recover_complete(void *context)
{ {
struct at86rf230_state_change *ctx = context; struct at86rf230_state_change *ctx = context;
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
lp->is_tx = 0;
at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
ieee802154_wake_queue(lp->hw);
if (ctx->free) if (ctx->free)
kfree(ctx); 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 static inline void
...@@ -892,14 +902,12 @@ at86rf230_xmit_start(void *context) ...@@ -892,14 +902,12 @@ at86rf230_xmit_start(void *context)
struct at86rf230_local *lp = ctx->lp; struct at86rf230_local *lp = ctx->lp;
/* check if we change from off state */ /* check if we change from off state */
if (lp->is_tx_from_off) { if (lp->is_tx_from_off)
lp->is_tx_from_off = false;
at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON, at86rf230_async_state_change(lp, ctx, STATE_TX_ARET_ON,
at86rf230_write_frame); at86rf230_write_frame);
} else { else
at86rf230_async_state_change(lp, ctx, STATE_TX_ON, at86rf230_async_state_change(lp, ctx, STATE_TX_ON,
at86rf230_xmit_tx_on); at86rf230_xmit_tx_on);
}
} }
static int static int
...@@ -923,6 +931,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb) ...@@ -923,6 +931,7 @@ at86rf230_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF, at86rf230_async_state_change(lp, ctx, STATE_TRX_OFF,
at86rf230_xmit_start); at86rf230_xmit_start);
} else { } else {
lp->is_tx_from_off = false;
at86rf230_xmit_start(ctx); at86rf230_xmit_start(ctx);
} }
......
...@@ -310,6 +310,7 @@ mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg) ...@@ -310,6 +310,7 @@ mrf24j40_short_reg_writeable(struct device *dev, unsigned int reg)
case REG_TRISGPIO: case REG_TRISGPIO:
case REG_GPIO: case REG_GPIO:
case REG_RFCTL: case REG_RFCTL:
case REG_SECCR2:
case REG_SLPACK: case REG_SLPACK:
case REG_BBREG0: case REG_BBREG0:
case REG_BBREG1: case REG_BBREG1:
......
...@@ -75,6 +75,8 @@ ...@@ -75,6 +75,8 @@
#define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \ #define LOWPAN_IPHC_MAX_HC_BUF_LEN (sizeof(struct ipv6hdr) + \
LOWPAN_IPHC_MAX_HEADER_LEN + \ LOWPAN_IPHC_MAX_HEADER_LEN + \
LOWPAN_NHC_MAX_HDR_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_IPV6 0x41 /* 01000001 = 65 */
#define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */ #define LOWPAN_DISPATCH_IPHC 0x60 /* 011xxxxx = ... */
...@@ -98,9 +100,39 @@ enum lowpan_lltypes { ...@@ -98,9 +100,39 @@ enum lowpan_lltypes {
LOWPAN_LLTYPE_IEEE802154, 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 { struct lowpan_priv {
enum lowpan_lltypes lltype; enum lowpan_lltypes lltype;
struct dentry *iface_debugfs; struct dentry *iface_debugfs;
struct lowpan_iphc_ctx_table ctx;
/* must be last */ /* must be last */
u8 priv[0] __aligned(sizeof(void *)); u8 priv[0] __aligned(sizeof(void *));
......
...@@ -25,6 +25,7 @@ ...@@ -25,6 +25,7 @@
#ifndef __HCI_CORE_H #ifndef __HCI_CORE_H
#define __HCI_CORE_H #define __HCI_CORE_H
#include <linux/leds.h>
#include <net/bluetooth/hci.h> #include <net/bluetooth/hci.h>
#include <net/bluetooth/hci_sock.h> #include <net/bluetooth/hci_sock.h>
...@@ -396,6 +397,8 @@ struct hci_dev { ...@@ -396,6 +397,8 @@ struct hci_dev {
struct delayed_work rpa_expired; struct delayed_work rpa_expired;
bdaddr_t rpa; bdaddr_t rpa;
struct led_trigger *power_led;
int (*open)(struct hci_dev *hdev); int (*open)(struct hci_dev *hdev);
int (*close)(struct hci_dev *hdev); int (*close)(struct hci_dev *hdev);
int (*flush)(struct hci_dev *hdev); int (*flush)(struct hci_dev *hdev);
......
...@@ -247,8 +247,9 @@ struct ieee802154_ops { ...@@ -247,8 +247,9 @@ struct ieee802154_ops {
*/ */
static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb) static inline __le16 ieee802154_get_fc_from_skb(const struct sk_buff *skb)
{ {
/* return some invalid fc on failure */ /* check if we can fc at skb_mac_header of sk buffer */
if (unlikely(skb->len < 2)) { if (unlikely(!skb_mac_header_was_set(skb) ||
(skb_tail_pointer(skb) - skb_mac_header(skb)) < 2)) {
WARN_ON(1); WARN_ON(1);
return cpu_to_le16(0); return cpu_to_le16(0);
} }
......
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
int lowpan_register_netdevice(struct net_device *dev, int lowpan_register_netdevice(struct net_device *dev,
enum lowpan_lltypes lltype) enum lowpan_lltypes lltype)
{ {
int ret; int i, ret;
dev->addr_len = EUI64_ADDR_LEN; dev->addr_len = EUI64_ADDR_LEN;
dev->type = ARPHRD_6LOWPAN; dev->type = ARPHRD_6LOWPAN;
...@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev, ...@@ -29,6 +29,10 @@ int lowpan_register_netdevice(struct net_device *dev,
lowpan_priv(dev)->lltype = lltype; 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); ret = register_netdevice(dev);
if (ret < 0) if (ret < 0)
return ret; return ret;
...@@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev) ...@@ -68,6 +72,32 @@ void lowpan_unregister_netdev(struct net_device *dev)
} }
EXPORT_SYMBOL(lowpan_unregister_netdev); 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) static int __init lowpan_module_init(void)
{ {
int ret; int ret;
...@@ -76,6 +106,12 @@ static int __init lowpan_module_init(void) ...@@ -76,6 +106,12 @@ static int __init lowpan_module_init(void)
if (ret < 0) if (ret < 0)
return ret; return ret;
ret = register_netdevice_notifier(&lowpan_notifier);
if (ret < 0) {
lowpan_debugfs_exit();
return ret;
}
request_module_nowait("ipv6"); request_module_nowait("ipv6");
request_module_nowait("nhc_dest"); request_module_nowait("nhc_dest");
...@@ -92,6 +128,7 @@ static int __init lowpan_module_init(void) ...@@ -92,6 +128,7 @@ static int __init lowpan_module_init(void)
static void __exit lowpan_module_exit(void) static void __exit lowpan_module_exit(void)
{ {
lowpan_debugfs_exit(); lowpan_debugfs_exit();
unregister_netdevice_notifier(&lowpan_notifier);
} }
module_init(lowpan_module_init); module_init(lowpan_module_init);
......
...@@ -16,19 +16,266 @@ ...@@ -16,19 +16,266 @@
#include "6lowpan_i.h" #include "6lowpan_i.h"
#define LOWPAN_DEBUGFS_CTX_PFX_NUM_ARGS 8
static struct dentry *lowpan_debugfs; 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) int lowpan_dev_debugfs_init(struct net_device *dev)
{ {
struct lowpan_priv *lpriv = lowpan_priv(dev); struct lowpan_priv *lpriv = lowpan_priv(dev);
struct dentry *contexts, *dentry;
int ret, i;
/* creating the root */ /* creating the root */
lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs); lpriv->iface_debugfs = debugfs_create_dir(dev->name, lowpan_debugfs);
if (!lpriv->iface_debugfs) if (!lpriv->iface_debugfs)
goto fail; 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; return 0;
remove_root:
lowpan_dev_debugfs_exit(dev);
fail: fail:
return -EINVAL; return -EINVAL;
} }
......
This diff is collapsed.
...@@ -69,6 +69,15 @@ config BT_6LOWPAN ...@@ -69,6 +69,15 @@ config BT_6LOWPAN
help help
IPv6 compression over Bluetooth Low Energy. 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 config BT_SELFTEST
bool "Bluetooth self testing support" bool "Bluetooth self testing support"
depends on BT && DEBUG_KERNEL depends on BT && DEBUG_KERNEL
......
...@@ -17,6 +17,7 @@ bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \ ...@@ -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_BREDR) += sco.o
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.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_DEBUGFS) += hci_debugfs.o
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "hci_request.h" #include "hci_request.h"
#include "hci_debugfs.h" #include "hci_debugfs.h"
#include "smp.h" #include "smp.h"
#include "leds.h"
static void hci_rx_work(struct work_struct *work); static void hci_rx_work(struct work_struct *work);
static void hci_cmd_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) ...@@ -1395,6 +1396,7 @@ static int hci_dev_do_open(struct hci_dev *hdev)
hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); hci_dev_set_flag(hdev, HCI_RPA_EXPIRED);
set_bit(HCI_UP, &hdev->flags); set_bit(HCI_UP, &hdev->flags);
hci_sock_dev_event(hdev, HCI_DEV_UP); hci_sock_dev_event(hdev, HCI_DEV_UP);
hci_leds_update_powered(hdev, true);
if (!hci_dev_test_flag(hdev, HCI_SETUP) && if (!hci_dev_test_flag(hdev, HCI_SETUP) &&
!hci_dev_test_flag(hdev, HCI_CONFIG) && !hci_dev_test_flag(hdev, HCI_CONFIG) &&
!hci_dev_test_flag(hdev, HCI_UNCONFIGURED) && !hci_dev_test_flag(hdev, HCI_UNCONFIGURED) &&
...@@ -1532,6 +1534,8 @@ int hci_dev_do_close(struct hci_dev *hdev) ...@@ -1532,6 +1534,8 @@ int hci_dev_do_close(struct hci_dev *hdev)
return 0; return 0;
} }
hci_leds_update_powered(hdev, false);
/* Flush RX and TX works */ /* Flush RX and TX works */
flush_work(&hdev->tx_work); flush_work(&hdev->tx_work);
flush_work(&hdev->rx_work); flush_work(&hdev->rx_work);
...@@ -2017,6 +2021,7 @@ static void hci_power_on(struct work_struct *work) ...@@ -2017,6 +2021,7 @@ static void hci_power_on(struct work_struct *work)
if (test_bit(HCI_UP, &hdev->flags) && if (test_bit(HCI_UP, &hdev->flags) &&
hci_dev_test_flag(hdev, HCI_MGMT) && hci_dev_test_flag(hdev, HCI_MGMT) &&
hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) { hci_dev_test_and_clear_flag(hdev, HCI_AUTO_OFF)) {
cancel_delayed_work(&hdev->power_off);
hci_req_sync_lock(hdev); hci_req_sync_lock(hdev);
err = __hci_req_hci_power_on(hdev); err = __hci_req_hci_power_on(hdev);
hci_req_sync_unlock(hdev); hci_req_sync_unlock(hdev);
...@@ -3067,6 +3072,8 @@ int hci_register_dev(struct hci_dev *hdev) ...@@ -3067,6 +3072,8 @@ int hci_register_dev(struct hci_dev *hdev)
if (error < 0) if (error < 0)
goto err_wqueue; goto err_wqueue;
hci_leds_init(hdev);
hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev, hdev->rfkill = rfkill_alloc(hdev->name, &hdev->dev,
RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops, RFKILL_TYPE_BLUETOOTH, &hci_rfkill_ops,
hdev); hdev);
......
/*
* 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");
}
/*
* 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
...@@ -207,7 +207,7 @@ static int lowpan_device_event(struct notifier_block *unused, ...@@ -207,7 +207,7 @@ static int lowpan_device_event(struct notifier_block *unused,
struct net_device *wdev = netdev_notifier_info_to_dev(ptr); struct net_device *wdev = netdev_notifier_info_to_dev(ptr);
if (wdev->type != ARPHRD_IEEE802154) if (wdev->type != ARPHRD_IEEE802154)
goto out; return NOTIFY_DONE;
switch (event) { switch (event) {
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
...@@ -219,11 +219,10 @@ static int lowpan_device_event(struct notifier_block *unused, ...@@ -219,11 +219,10 @@ static int lowpan_device_event(struct notifier_block *unused,
lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL); lowpan_dellink(wdev->ieee802154_ptr->lowpan_dev, NULL);
break; break;
default: default:
break; return NOTIFY_DONE;
} }
out: return NOTIFY_OK;
return NOTIFY_DONE;
} }
static struct notifier_block lowpan_dev_notifier = { static struct notifier_block lowpan_dev_notifier = {
......
...@@ -218,7 +218,6 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw) ...@@ -218,7 +218,6 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
tasklet_kill(&local->tasklet); tasklet_kill(&local->tasklet);
flush_workqueue(local->workqueue); flush_workqueue(local->workqueue);
destroy_workqueue(local->workqueue);
rtnl_lock(); rtnl_lock();
...@@ -226,6 +225,7 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw) ...@@ -226,6 +225,7 @@ void ieee802154_unregister_hw(struct ieee802154_hw *hw)
rtnl_unlock(); rtnl_unlock();
destroy_workqueue(local->workqueue);
wpan_phy_unregister(local->phy); wpan_phy_unregister(local->phy);
} }
EXPORT_SYMBOL(ieee802154_unregister_hw); EXPORT_SYMBOL(ieee802154_unregister_hw);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment