mirror of
https://github.com/torvalds/linux.git
synced 2024-12-01 00:21:32 +00:00
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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
aefd3fb26e
@ -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/
|
||||
|
@ -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
|
||||
|
@ -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__
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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 },
|
||||
|
337
drivers/bluetooth/hci_ag6xx.c
Normal file
337
drivers/bluetooth/hci_ag6xx.c
Normal file
@ -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);
|
||||
}
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -342,6 +342,18 @@ static const struct regmap_config at86rf230_regmap_spi_config = {
|
||||
.precious_reg = at86rf230_reg_precious,
|
||||
};
|
||||
|
||||
static void
|
||||
at86rf230_async_error_recover_complete(void *context)
|
||||
{
|
||||
struct at86rf230_state_change *ctx = context;
|
||||
struct at86rf230_local *lp = ctx->lp;
|
||||
|
||||
if (ctx->free)
|
||||
kfree(ctx);
|
||||
|
||||
ieee802154_wake_queue(lp->hw);
|
||||
}
|
||||
|
||||
static void
|
||||
at86rf230_async_error_recover(void *context)
|
||||
{
|
||||
@ -349,10 +361,8 @@ at86rf230_async_error_recover(void *context)
|
||||
struct at86rf230_local *lp = ctx->lp;
|
||||
|
||||
lp->is_tx = 0;
|
||||
at86rf230_async_state_change(lp, ctx, STATE_RX_AACK_ON, NULL);
|
||||
ieee802154_wake_queue(lp->hw);
|
||||
if (ctx->free)
|
||||
kfree(ctx);
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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 *));
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -16,19 +16,266 @@
|
||||
|
||||
#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;
|
||||
}
|
||||
|
@ -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 */
|
||||
@ -147,6 +148,9 @@
|
||||
(((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);
|
||||
|
||||
if (err)
|
||||
return -EINVAL;
|
||||
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;
|
||||
}
|
||||
} else {
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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);
|
||||
} 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_iphc_mcast_ctx_addr_compress(&hc_ptr,
|
||||
&dci_entry,
|
||||
&hdr->daddr);
|
||||
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);
|
||||
iphc1 |= lowpan_iphc_mcast_addr_compress(&hc_ptr,
|
||||
&hdr->daddr);
|
||||
}
|
||||
} else {
|
||||
if (dci) {
|
||||
iphc1 |= lowpan_compress_ctx_addr(&hc_ptr, &hdr->daddr,
|
||||
&dci_entry, daddr,
|
||||
false);
|
||||
iphc1 |= LOWPAN_IPHC_DAC;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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);
|
||||
|
74
net/bluetooth/leds.c
Normal file
74
net/bluetooth/leds.c
Normal file
@ -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");
|
||||
}
|
16
net/bluetooth/leds.h
Normal file
16
net/bluetooth/leds.h
Normal file
@ -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
|
@ -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 = {
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user