mirror of
https://github.com/edk2-porting/linux-next.git
synced 2024-12-26 06:04:14 +08: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 2015-07-30 Here's a set of Bluetooth & 802.15.4 patches intended for the 4.3 kernel. - Cleanups & fixes to mac802154 - Refactoring of Intel Bluetooth HCI driver - Various coding style fixes to Bluetooth HCI drivers - Support for Intel Lightning Peak Bluetooth devices - Generic class code in interface descriptor in btusb to match more HW - Refactoring of Bluetooth HS code together with a new config option - Support for BCM4330B1 Broadcom UART controller 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
29a3060aa7
@ -132,6 +132,7 @@ config BT_HCIUART_3WIRE
|
||||
config BT_HCIUART_INTEL
|
||||
bool "Intel protocol support"
|
||||
depends on BT_HCIUART
|
||||
select BT_HCIUART_H4
|
||||
select BT_INTEL
|
||||
help
|
||||
The Intel protocol support enables Bluetooth HCI over serial
|
||||
|
@ -492,7 +492,7 @@ static int bfusb_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
||||
|
@ -427,7 +427,7 @@ static int bt3c_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
case HCI_SCODATA_PKT:
|
||||
hdev->stat.sco_tx++;
|
||||
break;
|
||||
};
|
||||
}
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
||||
|
@ -34,6 +34,7 @@
|
||||
|
||||
#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
|
||||
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
|
||||
#define BDADDR_BCM4330B1 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb1, 0x30, 0x43}})
|
||||
|
||||
int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
{
|
||||
@ -66,9 +67,13 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
*
|
||||
* The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller
|
||||
* with waiting for configuration state.
|
||||
*
|
||||
* The address 43:30:B1:00:00:00 indicates a BCM4330B1 controller
|
||||
* with waiting for configuration state.
|
||||
*/
|
||||
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4324B3)) {
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4324B3) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4330B1)) {
|
||||
BT_INFO("%s: BCM: Using default device address (%pMR)",
|
||||
hdev->name, &bda->bdaddr);
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
@ -241,6 +246,7 @@ static const struct {
|
||||
u16 subver;
|
||||
const char *name;
|
||||
} bcm_uart_subver_table[] = {
|
||||
{ 0x4103, "BCM4330B1" }, /* 002.001.003 */
|
||||
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
||||
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
|
||||
{ 0x610c, "BCM4354" }, /* 003.001.012 */
|
||||
|
@ -89,6 +89,86 @@ int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_set_bdaddr);
|
||||
|
||||
void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 type = 0x00;
|
||||
|
||||
BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Reset after hardware error failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Retrieving Intel exception info failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->len != 13) {
|
||||
BT_ERR("%s: Exception info size mismatch", hdev->name);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1));
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_hw_error);
|
||||
|
||||
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
|
||||
{
|
||||
const char *variant;
|
||||
|
||||
switch (ver->fw_variant) {
|
||||
case 0x06:
|
||||
variant = "Bootloader";
|
||||
break;
|
||||
case 0x23:
|
||||
variant = "Firmware";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name,
|
||||
variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
|
||||
ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_version_info);
|
||||
|
||||
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
||||
const void *param)
|
||||
{
|
||||
while (plen > 0) {
|
||||
struct sk_buff *skb;
|
||||
u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen;
|
||||
|
||||
cmd_param[0] = fragment_type;
|
||||
memcpy(cmd_param + 1, param, fragment_len);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1,
|
||||
cmd_param, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
plen -= fragment_len;
|
||||
param += fragment_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btintel_secure_send);
|
||||
|
||||
MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
|
||||
MODULE_DESCRIPTION("Bluetooth support for Intel devices ver " VERSION);
|
||||
MODULE_VERSION(VERSION);
|
||||
|
@ -73,6 +73,11 @@ struct intel_secure_send_result {
|
||||
|
||||
int btintel_check_bdaddr(struct hci_dev *hdev);
|
||||
int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
void btintel_hw_error(struct hci_dev *hdev, u8 code);
|
||||
|
||||
void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver);
|
||||
int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type, u32 plen,
|
||||
const void *param);
|
||||
|
||||
#else
|
||||
|
||||
@ -86,4 +91,18 @@ static inline int btintel_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdadd
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline void btintel_hw_error(struct hci_dev *hdev, u8 code)
|
||||
{
|
||||
}
|
||||
|
||||
static void btintel_version_info(struct hci_dev *hdev, struct intel_version *ver)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int btintel_secure_send(struct hci_dev *hdev, u8 fragment_type,
|
||||
u32 plen, const void *param)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -95,10 +95,10 @@ struct btmrvl_private {
|
||||
struct btmrvl_device btmrvl_dev;
|
||||
struct btmrvl_adapter *adapter;
|
||||
struct btmrvl_thread main_thread;
|
||||
int (*hw_host_to_card) (struct btmrvl_private *priv,
|
||||
int (*hw_host_to_card)(struct btmrvl_private *priv,
|
||||
u8 *payload, u16 nb);
|
||||
int (*hw_wakeup_firmware) (struct btmrvl_private *priv);
|
||||
int (*hw_process_int_status) (struct btmrvl_private *priv);
|
||||
int (*hw_wakeup_firmware)(struct btmrvl_private *priv);
|
||||
int (*hw_process_int_status)(struct btmrvl_private *priv);
|
||||
void (*firmware_dump)(struct btmrvl_private *priv);
|
||||
spinlock_t driver_lock; /* spinlock used by driver */
|
||||
#ifdef CONFIG_DEBUG_FS
|
||||
|
@ -68,6 +68,9 @@ static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth AMP device */
|
||||
{ USB_DEVICE_INFO(0xe0, 0x01, 0x04), .driver_info = BTUSB_AMP },
|
||||
|
||||
/* Generic Bluetooth USB interface */
|
||||
{ USB_INTERFACE_INFO(0xe0, 0x01, 0x01) },
|
||||
|
||||
/* Apple-specific (Broadcom) devices */
|
||||
{ USB_VENDOR_AND_INTERFACE_INFO(0x05ac, 0xff, 0x01, 0x01),
|
||||
.driver_info = BTUSB_BCM_APPLE },
|
||||
@ -1878,51 +1881,6 @@ static int btusb_send_frame_intel(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
static int btusb_intel_secure_send(struct hci_dev *hdev, u8 fragment_type,
|
||||
u32 plen, const void *param)
|
||||
{
|
||||
while (plen > 0) {
|
||||
struct sk_buff *skb;
|
||||
u8 cmd_param[253], fragment_len = (plen > 252) ? 252 : plen;
|
||||
|
||||
cmd_param[0] = fragment_type;
|
||||
memcpy(cmd_param + 1, param, fragment_len);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc09, fragment_len + 1,
|
||||
cmd_param, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
plen -= fragment_len;
|
||||
param += fragment_len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btusb_intel_version_info(struct hci_dev *hdev,
|
||||
struct intel_version *ver)
|
||||
{
|
||||
const char *variant;
|
||||
|
||||
switch (ver->fw_variant) {
|
||||
case 0x06:
|
||||
variant = "Bootloader";
|
||||
break;
|
||||
case 0x23:
|
||||
variant = "Firmware";
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
BT_INFO("%s: %s revision %u.%u build %u week %u %u", hdev->name,
|
||||
variant, ver->fw_revision >> 4, ver->fw_revision & 0x0f,
|
||||
ver->fw_build_num, ver->fw_build_ww, 2000 + ver->fw_build_yy);
|
||||
}
|
||||
|
||||
static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
{
|
||||
static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
|
||||
@ -1984,7 +1942,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btusb_intel_version_info(hdev, ver);
|
||||
btintel_version_info(hdev, ver);
|
||||
|
||||
/* The firmware variant determines if the device is in bootloader
|
||||
* mode or is running operational firmware. The value 0x06 identifies
|
||||
@ -2104,7 +2062,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
/* Start the firmware download transaction with the Init fragment
|
||||
* represented by the 128 bytes of CSS header.
|
||||
*/
|
||||
err = btusb_intel_secure_send(hdev, 0x00, 128, fw->data);
|
||||
err = btintel_secure_send(hdev, 0x00, 128, fw->data);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware header (%d)",
|
||||
hdev->name, err);
|
||||
@ -2114,7 +2072,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
/* Send the 256 bytes of public key information from the firmware
|
||||
* as the PKey fragment.
|
||||
*/
|
||||
err = btusb_intel_secure_send(hdev, 0x03, 256, fw->data + 128);
|
||||
err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware public key (%d)",
|
||||
hdev->name, err);
|
||||
@ -2124,7 +2082,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
/* Send the 256 bytes of signature information from the firmware
|
||||
* as the Sign fragment.
|
||||
*/
|
||||
err = btusb_intel_secure_send(hdev, 0x02, 256, fw->data + 388);
|
||||
err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware signature (%d)",
|
||||
hdev->name, err);
|
||||
@ -2148,8 +2106,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
* firmware data buffer as a single Data fragement.
|
||||
*/
|
||||
if (!(frag_len % 4)) {
|
||||
err = btusb_intel_secure_send(hdev, 0x01, frag_len,
|
||||
fw_ptr);
|
||||
err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware data (%d)",
|
||||
hdev->name, err);
|
||||
@ -2291,39 +2248,6 @@ done:
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void btusb_hw_error_intel(struct hci_dev *hdev, u8 code)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 type = 0x00;
|
||||
|
||||
BT_ERR("%s: Hardware error 0x%2.2x", hdev->name, code);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Reset after hardware error failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc22, 1, &type, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Retrieving Intel exception info failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->len != 13) {
|
||||
BT_ERR("%s: Exception info size mismatch", hdev->name);
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
BT_ERR("%s: Exception info %s", hdev->name, (char *)(skb->data + 1));
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
static int btusb_shutdown_intel(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@ -2783,7 +2707,7 @@ static int btusb_probe(struct usb_interface *intf,
|
||||
if (id->driver_info & BTUSB_INTEL_NEW) {
|
||||
hdev->send = btusb_send_frame_intel;
|
||||
hdev->setup = btusb_setup_intel_new;
|
||||
hdev->hw_error = btusb_hw_error_intel;
|
||||
hdev->hw_error = btintel_hw_error;
|
||||
hdev->set_bdaddr = btintel_set_bdaddr;
|
||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ static void dtl1_control(struct dtl1_info *info, struct sk_buff *skb)
|
||||
int i;
|
||||
|
||||
printk(KERN_INFO "Bluetooth: Nokia control data =");
|
||||
for (i = 0; i < skb->len; i++) {
|
||||
for (i = 0; i < skb->len; i++)
|
||||
printk(" %02x", skb->data[i]);
|
||||
}
|
||||
|
||||
printk("\n");
|
||||
|
||||
/* transition to active state */
|
||||
@ -406,7 +406,7 @@ static int dtl1_hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
break;
|
||||
default:
|
||||
return -EILSEQ;
|
||||
};
|
||||
}
|
||||
|
||||
nsh.zero = 0;
|
||||
nsh.len = skb->len;
|
||||
|
@ -75,7 +75,7 @@ struct h5 {
|
||||
size_t rx_pending; /* Expecting more bytes */
|
||||
u8 rx_ack; /* Last ack number received */
|
||||
|
||||
int (*rx_func) (struct hci_uart *hu, u8 c);
|
||||
int (*rx_func)(struct hci_uart *hu, u8 c);
|
||||
|
||||
struct timer_list timer; /* Retransmission timer */
|
||||
|
||||
|
@ -24,8 +24,569 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/wait.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "hci_uart.h"
|
||||
#include "btintel.h"
|
||||
|
||||
#define STATE_BOOTLOADER 0
|
||||
#define STATE_DOWNLOADING 1
|
||||
#define STATE_FIRMWARE_LOADED 2
|
||||
#define STATE_FIRMWARE_FAILED 3
|
||||
#define STATE_BOOTING 4
|
||||
|
||||
struct intel_data {
|
||||
struct sk_buff *rx_skb;
|
||||
struct sk_buff_head txq;
|
||||
unsigned long flags;
|
||||
};
|
||||
|
||||
static int intel_open(struct hci_uart *hu)
|
||||
{
|
||||
struct intel_data *intel;
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
intel = kzalloc(sizeof(*intel), GFP_KERNEL);
|
||||
if (!intel)
|
||||
return -ENOMEM;
|
||||
|
||||
skb_queue_head_init(&intel->txq);
|
||||
|
||||
hu->priv = intel;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_close(struct hci_uart *hu)
|
||||
{
|
||||
struct intel_data *intel = hu->priv;
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
skb_queue_purge(&intel->txq);
|
||||
kfree_skb(intel->rx_skb);
|
||||
kfree(intel);
|
||||
|
||||
hu->priv = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_flush(struct hci_uart *hu)
|
||||
{
|
||||
struct intel_data *intel = hu->priv;
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
skb_queue_purge(&intel->txq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct hci_event_hdr *hdr;
|
||||
struct hci_ev_cmd_complete *evt;
|
||||
|
||||
skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = (struct hci_event_hdr *)skb_put(skb, sizeof(*hdr));
|
||||
hdr->evt = HCI_EV_CMD_COMPLETE;
|
||||
hdr->plen = sizeof(*evt) + 1;
|
||||
|
||||
evt = (struct hci_ev_cmd_complete *)skb_put(skb, sizeof(*evt));
|
||||
evt->ncmd = 0x01;
|
||||
evt->opcode = cpu_to_le16(opcode);
|
||||
|
||||
*skb_put(skb, 1) = 0x00;
|
||||
|
||||
bt_cb(skb)->pkt_type = HCI_EVENT_PKT;
|
||||
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static int intel_setup(struct hci_uart *hu)
|
||||
{
|
||||
static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
|
||||
0x00, 0x08, 0x04, 0x00 };
|
||||
struct intel_data *intel = hu->priv;
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
struct sk_buff *skb;
|
||||
struct intel_version *ver;
|
||||
struct intel_boot_params *params;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
char fwname[64];
|
||||
u32 frag_len;
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hu->hdev->set_bdaddr = btintel_set_bdaddr;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
set_bit(STATE_BOOTLOADER, &intel->flags);
|
||||
|
||||
/* Read the Intel version information to determine if the device
|
||||
* is in bootloader mode or if it already has operational firmware
|
||||
* loaded.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Reading Intel version information failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*ver)) {
|
||||
BT_ERR("%s: Intel version event size mismatch", hdev->name);
|
||||
kfree_skb(skb);
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
ver = (struct intel_version *)skb->data;
|
||||
if (ver->status) {
|
||||
BT_ERR("%s: Intel version command failure (%02x)",
|
||||
hdev->name, ver->status);
|
||||
err = -bt_to_errno(ver->status);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* The hardware platform number has a fixed value of 0x37 and
|
||||
* for now only accept this single value.
|
||||
*/
|
||||
if (ver->hw_platform != 0x37) {
|
||||
BT_ERR("%s: Unsupported Intel hardware platform (%u)",
|
||||
hdev->name, ver->hw_platform);
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* At the moment only the hardware variant iBT 3.0 (LnP/SfP) is
|
||||
* supported by this firmware loading method. This check has been
|
||||
* put in place to ensure correct forward compatibility options
|
||||
* when newer hardware variants come along.
|
||||
*/
|
||||
if (ver->hw_variant != 0x0b) {
|
||||
BT_ERR("%s: Unsupported Intel hardware variant (%u)",
|
||||
hdev->name, ver->hw_variant);
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
btintel_version_info(hdev, ver);
|
||||
|
||||
/* The firmware variant determines if the device is in bootloader
|
||||
* mode or is running operational firmware. The value 0x06 identifies
|
||||
* the bootloader and the value 0x23 identifies the operational
|
||||
* firmware.
|
||||
*
|
||||
* When the operational firmware is already present, then only
|
||||
* the check for valid Bluetooth device address is needed. This
|
||||
* determines if the device will be added as configured or
|
||||
* unconfigured controller.
|
||||
*
|
||||
* It is not possible to use the Secure Boot Parameters in this
|
||||
* case since that command is only available in bootloader mode.
|
||||
*/
|
||||
if (ver->fw_variant == 0x23) {
|
||||
kfree_skb(skb);
|
||||
clear_bit(STATE_BOOTLOADER, &intel->flags);
|
||||
btintel_check_bdaddr(hdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If the device is not in bootloader mode, then the only possible
|
||||
* choice is to return an error and abort the device initialization.
|
||||
*/
|
||||
if (ver->fw_variant != 0x06) {
|
||||
BT_ERR("%s: Unsupported Intel firmware variant (%u)",
|
||||
hdev->name, ver->fw_variant);
|
||||
kfree_skb(skb);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
/* Read the secure boot parameters to identify the operating
|
||||
* details of the bootloader.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc0d, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Reading Intel boot parameters failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(*params)) {
|
||||
BT_ERR("%s: Intel boot parameters size mismatch", hdev->name);
|
||||
kfree_skb(skb);
|
||||
return -EILSEQ;
|
||||
}
|
||||
|
||||
params = (struct intel_boot_params *)skb->data;
|
||||
if (params->status) {
|
||||
BT_ERR("%s: Intel boot parameters command failure (%02x)",
|
||||
hdev->name, params->status);
|
||||
err = -bt_to_errno(params->status);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
BT_INFO("%s: Device revision is %u", hdev->name,
|
||||
le16_to_cpu(params->dev_revid));
|
||||
|
||||
BT_INFO("%s: Secure boot is %s", hdev->name,
|
||||
params->secure_boot ? "enabled" : "disabled");
|
||||
|
||||
BT_INFO("%s: Minimum firmware build %u week %u %u", hdev->name,
|
||||
params->min_fw_build_nn, params->min_fw_build_cw,
|
||||
2000 + params->min_fw_build_yy);
|
||||
|
||||
/* It is required that every single firmware fragment is acknowledged
|
||||
* with a command complete event. If the boot parameters indicate
|
||||
* that this bootloader does not send them, then abort the setup.
|
||||
*/
|
||||
if (params->limited_cce != 0x00) {
|
||||
BT_ERR("%s: Unsupported Intel firmware loading method (%u)",
|
||||
hdev->name, params->limited_cce);
|
||||
kfree_skb(skb);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* If the OTP has no valid Bluetooth device address, then there will
|
||||
* also be no valid address for the operational firmware.
|
||||
*/
|
||||
if (!bacmp(¶ms->otp_bdaddr, BDADDR_ANY)) {
|
||||
BT_INFO("%s: No device address configured", hdev->name);
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
}
|
||||
|
||||
/* With this Intel bootloader only the hardware variant and device
|
||||
* revision information are used to select the right firmware.
|
||||
*
|
||||
* Currently this bootloader support is limited to hardware variant
|
||||
* iBT 3.0 (LnP/SfP) which is identified by the value 11 (0x0b).
|
||||
*/
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.sfi",
|
||||
le16_to_cpu(params->dev_revid));
|
||||
|
||||
err = request_firmware(&fw, fwname, &hdev->dev);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to load Intel firmware file (%d)",
|
||||
hdev->name, err);
|
||||
kfree_skb(skb);
|
||||
return err;
|
||||
}
|
||||
|
||||
BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
if (fw->size < 644) {
|
||||
BT_ERR("%s: Invalid size of firmware file (%zu)",
|
||||
hdev->name, fw->size);
|
||||
err = -EBADF;
|
||||
goto done;
|
||||
}
|
||||
|
||||
set_bit(STATE_DOWNLOADING, &intel->flags);
|
||||
|
||||
/* Start the firmware download transaction with the Init fragment
|
||||
* represented by the 128 bytes of CSS header.
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x00, 128, fw->data);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware header (%d)",
|
||||
hdev->name, err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Send the 256 bytes of public key information from the firmware
|
||||
* as the PKey fragment.
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x03, 256, fw->data + 128);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware public key (%d)",
|
||||
hdev->name, err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Send the 256 bytes of signature information from the firmware
|
||||
* as the Sign fragment.
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x02, 256, fw->data + 388);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware signature (%d)",
|
||||
hdev->name, err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
fw_ptr = fw->data + 644;
|
||||
frag_len = 0;
|
||||
|
||||
while (fw_ptr - fw->data < fw->size) {
|
||||
struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
|
||||
|
||||
frag_len += sizeof(*cmd) + cmd->plen;
|
||||
|
||||
BT_DBG("%s: patching %td/%zu", hdev->name,
|
||||
(fw_ptr - fw->data), fw->size);
|
||||
|
||||
/* The parameter length of the secure send command requires
|
||||
* a 4 byte alignment. It happens so that the firmware file
|
||||
* contains proper Intel_NOP commands to align the fragments
|
||||
* as needed.
|
||||
*
|
||||
* Send set of commands with 4 byte alignment from the
|
||||
* firmware data buffer as a single Data fragement.
|
||||
*/
|
||||
if (frag_len % 4)
|
||||
continue;
|
||||
|
||||
/* Send each command from the firmware data buffer as
|
||||
* a single Data fragment.
|
||||
*/
|
||||
err = btintel_secure_send(hdev, 0x01, frag_len, fw_ptr);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware data (%d)",
|
||||
hdev->name, err);
|
||||
goto done;
|
||||
}
|
||||
|
||||
fw_ptr += frag_len;
|
||||
frag_len = 0;
|
||||
}
|
||||
|
||||
set_bit(STATE_FIRMWARE_LOADED, &intel->flags);
|
||||
|
||||
BT_INFO("%s: Waiting for firmware download to complete", hdev->name);
|
||||
|
||||
/* Before switching the device into operational mode and with that
|
||||
* booting the loaded firmware, wait for the bootloader notification
|
||||
* that all fragments have been successfully received.
|
||||
*
|
||||
* When the event processing receives the notification, then the
|
||||
* STATE_DOWNLOADING flag will be cleared.
|
||||
*
|
||||
* The firmware loading should not take longer than 5 seconds
|
||||
* and thus just timeout if that happens and fail the setup
|
||||
* of this device.
|
||||
*/
|
||||
err = wait_on_bit_timeout(&intel->flags, STATE_DOWNLOADING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(5000));
|
||||
if (err == 1) {
|
||||
BT_ERR("%s: Firmware loading interrupted", hdev->name);
|
||||
err = -EINTR;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
BT_ERR("%s: Firmware loading timeout", hdev->name);
|
||||
err = -ETIMEDOUT;
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (test_bit(STATE_FIRMWARE_FAILED, &intel->flags)) {
|
||||
BT_ERR("%s: Firmware loading failed", hdev->name);
|
||||
err = -ENOEXEC;
|
||||
goto done;
|
||||
}
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||
|
||||
BT_INFO("%s: Firmware loaded in %llu usecs", hdev->name, duration);
|
||||
|
||||
done:
|
||||
release_firmware(fw);
|
||||
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
set_bit(STATE_BOOTING, &intel->flags);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc01, sizeof(reset_param), reset_param,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
/* The bootloader will not indicate when the device is ready. This
|
||||
* is done by the operational firmware sending bootup notification.
|
||||
*
|
||||
* Booting into operational firmware should not take longer than
|
||||
* 1 second. However if that happens, then just fail the setup
|
||||
* since something went wrong.
|
||||
*/
|
||||
BT_INFO("%s: Waiting for device to boot", hdev->name);
|
||||
|
||||
err = wait_on_bit_timeout(&intel->flags, STATE_BOOTING,
|
||||
TASK_INTERRUPTIBLE,
|
||||
msecs_to_jiffies(1000));
|
||||
|
||||
if (err == 1) {
|
||||
BT_ERR("%s: Device boot interrupted", hdev->name);
|
||||
return -EINTR;
|
||||
}
|
||||
|
||||
if (err) {
|
||||
BT_ERR("%s: Device boot timeout", hdev->name);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||
|
||||
BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
|
||||
|
||||
clear_bit(STATE_BOOTLOADER, &intel->flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct intel_data *intel = hu->priv;
|
||||
struct hci_event_hdr *hdr;
|
||||
|
||||
if (!test_bit(STATE_BOOTLOADER, &intel->flags))
|
||||
goto recv;
|
||||
|
||||
hdr = (void *)skb->data;
|
||||
|
||||
/* When the firmware loading completes the device sends
|
||||
* out a vendor specific event indicating the result of
|
||||
* the firmware loading.
|
||||
*/
|
||||
if (skb->len == 7 && hdr->evt == 0xff && hdr->plen == 0x05 &&
|
||||
skb->data[2] == 0x06) {
|
||||
if (skb->data[3] != 0x00)
|
||||
set_bit(STATE_FIRMWARE_FAILED, &intel->flags);
|
||||
|
||||
if (test_and_clear_bit(STATE_DOWNLOADING, &intel->flags) &&
|
||||
test_bit(STATE_FIRMWARE_LOADED, &intel->flags)) {
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(&intel->flags, STATE_DOWNLOADING);
|
||||
}
|
||||
|
||||
/* When switching to the operational firmware the device
|
||||
* sends a vendor specific event indicating that the bootup
|
||||
* completed.
|
||||
*/
|
||||
} else if (skb->len == 9 && hdr->evt == 0xff && hdr->plen == 0x07 &&
|
||||
skb->data[2] == 0x02) {
|
||||
if (test_and_clear_bit(STATE_BOOTING, &intel->flags)) {
|
||||
smp_mb__after_atomic();
|
||||
wake_up_bit(&intel->flags, STATE_BOOTING);
|
||||
}
|
||||
}
|
||||
recv:
|
||||
return hci_recv_frame(hdev, skb);
|
||||
}
|
||||
|
||||
static const struct h4_recv_pkt intel_recv_pkts[] = {
|
||||
{ H4_RECV_ACL, .recv = hci_recv_frame },
|
||||
{ H4_RECV_SCO, .recv = hci_recv_frame },
|
||||
{ H4_RECV_EVENT, .recv = intel_recv_event },
|
||||
};
|
||||
|
||||
static int intel_recv(struct hci_uart *hu, const void *data, int count)
|
||||
{
|
||||
struct intel_data *intel = hu->priv;
|
||||
|
||||
if (!test_bit(HCI_UART_REGISTERED, &hu->flags))
|
||||
return -EUNATCH;
|
||||
|
||||
intel->rx_skb = h4_recv_buf(hu->hdev, intel->rx_skb, data, count,
|
||||
intel_recv_pkts,
|
||||
ARRAY_SIZE(intel_recv_pkts));
|
||||
if (IS_ERR(intel->rx_skb)) {
|
||||
int err = PTR_ERR(intel->rx_skb);
|
||||
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
|
||||
intel->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static int intel_enqueue(struct hci_uart *hu, struct sk_buff *skb)
|
||||
{
|
||||
struct intel_data *intel = hu->priv;
|
||||
|
||||
BT_DBG("hu %p skb %p", hu, skb);
|
||||
|
||||
skb_queue_tail(&intel->txq, skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct sk_buff *intel_dequeue(struct hci_uart *hu)
|
||||
{
|
||||
struct intel_data *intel = hu->priv;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = skb_dequeue(&intel->txq);
|
||||
if (!skb)
|
||||
return skb;
|
||||
|
||||
if (test_bit(STATE_BOOTLOADER, &intel->flags) &&
|
||||
(bt_cb(skb)->pkt_type == HCI_COMMAND_PKT)) {
|
||||
struct hci_command_hdr *cmd = (void *)skb->data;
|
||||
__u16 opcode = le16_to_cpu(cmd->opcode);
|
||||
|
||||
/* When the 0xfc01 command is issued to boot into
|
||||
* the operational firmware, it will actually not
|
||||
* send a command complete event. To keep the flow
|
||||
* control working inject that event here.
|
||||
*/
|
||||
if (opcode == 0xfc01)
|
||||
inject_cmd_complete(hu->hdev, opcode);
|
||||
}
|
||||
|
||||
/* Prepend skb with frame type */
|
||||
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static const struct hci_uart_proto intel_proto = {
|
||||
.id = HCI_UART_INTEL,
|
||||
.name = "Intel",
|
||||
.init_speed = 115200,
|
||||
.open = intel_open,
|
||||
.close = intel_close,
|
||||
.flush = intel_flush,
|
||||
.setup = intel_setup,
|
||||
.recv = intel_recv,
|
||||
.enqueue = intel_enqueue,
|
||||
.dequeue = intel_dequeue,
|
||||
};
|
||||
|
||||
int __init intel_init(void)
|
||||
{
|
||||
return hci_uart_register_proto(&intel_proto);
|
||||
}
|
||||
|
||||
int __exit intel_deinit(void)
|
||||
{
|
||||
return hci_uart_unregister_proto(&intel_proto);
|
||||
}
|
||||
|
@ -770,7 +770,7 @@ static int __init hci_uart_init(void)
|
||||
|
||||
/* Register the tty discipline */
|
||||
|
||||
memset(&hci_uart_ldisc, 0, sizeof (hci_uart_ldisc));
|
||||
memset(&hci_uart_ldisc, 0, sizeof(hci_uart_ldisc));
|
||||
hci_uart_ldisc.magic = TTY_LDISC_MAGIC;
|
||||
hci_uart_ldisc.name = "n_hci";
|
||||
hci_uart_ldisc.open = hci_uart_tty_open;
|
||||
@ -804,6 +804,9 @@ static int __init hci_uart_init(void)
|
||||
#ifdef CONFIG_BT_HCIUART_3WIRE
|
||||
h5_init();
|
||||
#endif
|
||||
#ifdef CONFIG_BT_HCIUART_INTEL
|
||||
intel_init();
|
||||
#endif
|
||||
#ifdef CONFIG_BT_HCIUART_BCM
|
||||
bcm_init();
|
||||
#endif
|
||||
@ -830,6 +833,9 @@ static void __exit hci_uart_exit(void)
|
||||
#ifdef CONFIG_BT_HCIUART_3WIRE
|
||||
h5_deinit();
|
||||
#endif
|
||||
#ifdef CONFIG_BT_HCIUART_INTEL
|
||||
intel_deinit();
|
||||
#endif
|
||||
#ifdef CONFIG_BT_HCIUART_BCM
|
||||
bcm_deinit();
|
||||
#endif
|
||||
|
@ -167,6 +167,11 @@ int h5_init(void);
|
||||
int h5_deinit(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_INTEL
|
||||
int intel_init(void);
|
||||
int intel_deinit(void);
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_BCM
|
||||
int bcm_init(void);
|
||||
int bcm_deinit(void);
|
||||
|
@ -545,7 +545,9 @@ at86rf230_async_state_delay(void *context)
|
||||
}
|
||||
|
||||
/* Default delay is 1us in the most cases */
|
||||
tim = ktime_set(0, NSEC_PER_USEC);
|
||||
udelay(1);
|
||||
at86rf230_async_state_timer(&ctx->timer);
|
||||
return;
|
||||
|
||||
change:
|
||||
hrtimer_start(&ctx->timer, tim, HRTIMER_MODE_REL);
|
||||
|
@ -1151,7 +1151,6 @@ MODULE_DEVICE_TABLE(of, cc2520_of_ids);
|
||||
static struct spi_driver cc2520_driver = {
|
||||
.driver = {
|
||||
.name = "cc2520",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(cc2520_of_ids),
|
||||
},
|
||||
|
@ -812,7 +812,6 @@ MODULE_DEVICE_TABLE(spi, mrf24j40_ids);
|
||||
static struct spi_driver mrf24j40_driver = {
|
||||
.driver = {
|
||||
.name = "mrf24j40",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.id_table = mrf24j40_ids,
|
||||
|
@ -1297,7 +1297,7 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
|
||||
if (max >= to_multiplier * 8)
|
||||
return -EINVAL;
|
||||
|
||||
max_latency = (to_multiplier * 8 / max) - 1;
|
||||
max_latency = (to_multiplier * 4 / max) - 1;
|
||||
if (latency > 499 || latency > max_latency)
|
||||
return -EINVAL;
|
||||
|
||||
|
@ -55,6 +55,8 @@
|
||||
#define L2CAP_INFO_TIMEOUT msecs_to_jiffies(4000)
|
||||
#define L2CAP_MOVE_TIMEOUT msecs_to_jiffies(4000)
|
||||
#define L2CAP_MOVE_ERTX_TIMEOUT msecs_to_jiffies(60000)
|
||||
#define L2CAP_WAIT_ACK_POLL_PERIOD msecs_to_jiffies(200)
|
||||
#define L2CAP_WAIT_ACK_TIMEOUT msecs_to_jiffies(10000)
|
||||
|
||||
#define L2CAP_A2MP_DEFAULT_MTU 670
|
||||
|
||||
|
@ -34,6 +34,8 @@ struct cfg802154_ops {
|
||||
int type);
|
||||
void (*del_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
|
||||
struct net_device *dev);
|
||||
int (*suspend)(struct wpan_phy *wpan_phy);
|
||||
int (*resume)(struct wpan_phy *wpan_phy);
|
||||
int (*add_virtual_intf)(struct wpan_phy *wpan_phy,
|
||||
const char *name,
|
||||
unsigned char name_assign_type,
|
||||
|
@ -320,23 +320,6 @@ int ieee802154_register_hw(struct ieee802154_hw *hw);
|
||||
*/
|
||||
void ieee802154_unregister_hw(struct ieee802154_hw *hw);
|
||||
|
||||
/**
|
||||
* ieee802154_rx - receive frame
|
||||
*
|
||||
* Use this function to hand received frames to mac802154. The receive
|
||||
* buffer in @skb must start with an IEEE 802.15.4 header. In case of a
|
||||
* paged @skb is used, the driver is recommended to put the ieee802154
|
||||
* header of the frame on the linear part of the @skb to avoid memory
|
||||
* allocation and/or memcpy by the stack.
|
||||
*
|
||||
* This function may not be called in IRQ context. Calls to this function
|
||||
* for a single hardware must be synchronized against each other.
|
||||
*
|
||||
* @hw: the hardware this frame came in on
|
||||
* @skb: the buffer to receive, owned by mac802154 after this call
|
||||
*/
|
||||
void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb);
|
||||
|
||||
/**
|
||||
* ieee802154_rx_irqsafe - receive frame
|
||||
*
|
||||
|
@ -613,6 +613,8 @@ EXPORT_SYMBOL_GPL(lowpan_header_compress);
|
||||
|
||||
static int __init lowpan_module_init(void)
|
||||
{
|
||||
request_module_nowait("ipv6");
|
||||
|
||||
request_module_nowait("nhc_dest");
|
||||
request_module_nowait("nhc_fragment");
|
||||
request_module_nowait("nhc_hop");
|
||||
|
@ -859,18 +859,6 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
|
||||
SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
|
||||
SET_NETDEV_DEVTYPE(netdev, &bt_type);
|
||||
|
||||
err = register_netdev(netdev);
|
||||
if (err < 0) {
|
||||
BT_INFO("register_netdev failed %d", err);
|
||||
free_netdev(netdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
|
||||
netdev->ifindex, &chan->dst, chan->dst_type,
|
||||
&chan->src, chan->src_type);
|
||||
set_bit(__LINK_STATE_PRESENT, &netdev->state);
|
||||
|
||||
*dev = netdev_priv(netdev);
|
||||
(*dev)->netdev = netdev;
|
||||
(*dev)->hdev = chan->conn->hcon->hdev;
|
||||
@ -881,6 +869,21 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
|
||||
list_add_rcu(&(*dev)->list, &bt_6lowpan_devices);
|
||||
spin_unlock(&devices_lock);
|
||||
|
||||
err = register_netdev(netdev);
|
||||
if (err < 0) {
|
||||
BT_INFO("register_netdev failed %d", err);
|
||||
spin_lock(&devices_lock);
|
||||
list_del_rcu(&(*dev)->list);
|
||||
spin_unlock(&devices_lock);
|
||||
free_netdev(netdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d",
|
||||
netdev->ifindex, &chan->dst, chan->dst_type,
|
||||
&chan->src, chan->src_type);
|
||||
set_bit(__LINK_STATE_PRESENT, &netdev->state);
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
@ -53,6 +53,11 @@ source "net/bluetooth/cmtp/Kconfig"
|
||||
|
||||
source "net/bluetooth/hidp/Kconfig"
|
||||
|
||||
config BT_HS
|
||||
bool "Bluetooth High Speed (HS) features"
|
||||
depends on BT_BREDR
|
||||
default y
|
||||
|
||||
config BT_LE
|
||||
bool "Bluetooth Low Energy (LE) features"
|
||||
depends on BT
|
||||
|
@ -13,9 +13,10 @@ bluetooth_6lowpan-y := 6lowpan.o
|
||||
|
||||
bluetooth-y := af_bluetooth.o hci_core.o hci_conn.o hci_event.o mgmt.o \
|
||||
hci_sock.o hci_sysfs.o l2cap_core.o l2cap_sock.o smp.o lib.o \
|
||||
a2mp.o amp.o ecc.o hci_request.o mgmt_util.o
|
||||
ecc.o hci_request.o mgmt_util.o
|
||||
|
||||
bluetooth-$(CONFIG_BT_BREDR) += sco.o
|
||||
bluetooth-$(CONFIG_BT_HS) += a2mp.o amp.o
|
||||
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
|
||||
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
|
||||
@ -286,11 +287,21 @@ static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void read_local_amp_info_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
a2mp_send_getinfo_rsp(hdev);
|
||||
}
|
||||
|
||||
static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
struct a2mp_cmd *hdr)
|
||||
{
|
||||
struct a2mp_info_req *req = (void *) skb->data;
|
||||
struct hci_dev *hdev;
|
||||
struct hci_request hreq;
|
||||
int err = 0;
|
||||
|
||||
if (le16_to_cpu(hdr->len) < sizeof(*req))
|
||||
return -EINVAL;
|
||||
@ -311,7 +322,11 @@ static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
|
||||
}
|
||||
|
||||
set_bit(READ_LOC_AMP_INFO, &mgr->state);
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
|
||||
hci_req_init(&hreq, hdev);
|
||||
hci_req_add(&hreq, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
|
||||
err = hci_req_run(&hreq, read_local_amp_info_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getinfo_rsp(hdev);
|
||||
|
||||
done:
|
||||
if (hdev)
|
||||
|
@ -130,10 +130,29 @@ struct a2mp_physlink_rsp {
|
||||
#define A2MP_STATUS_SECURITY_VIOLATION 0x06
|
||||
|
||||
struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
int amp_mgr_put(struct amp_mgr *mgr);
|
||||
struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
||||
struct sk_buff *skb);
|
||||
void a2mp_discover_amp(struct l2cap_chan *chan);
|
||||
#else
|
||||
static inline int amp_mgr_put(struct amp_mgr *mgr)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void a2mp_discover_amp(struct l2cap_chan *chan)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void a2mp_send_getinfo_rsp(struct hci_dev *hdev);
|
||||
void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status);
|
||||
void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status);
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <crypto/hash.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
|
||||
@ -220,10 +221,49 @@ int phylink_gen_key(struct hci_conn *conn, u8 *data, u8 *len, u8 *type)
|
||||
return hmac_sha256(gamp_key, HCI_AMP_LINK_KEY_SIZE, "802b", 4, data);
|
||||
}
|
||||
|
||||
static void read_local_amp_assoc_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_amp_assoc *rp = (void *)skb->data;
|
||||
struct amp_assoc *assoc = &hdev->loc_assoc;
|
||||
size_t rem_len, frag_len;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
goto send_rsp;
|
||||
|
||||
frag_len = skb->len - sizeof(*rp);
|
||||
rem_len = __le16_to_cpu(rp->rem_len);
|
||||
|
||||
if (rem_len > frag_len) {
|
||||
BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len);
|
||||
|
||||
memcpy(assoc->data + assoc->offset, rp->frag, frag_len);
|
||||
assoc->offset += frag_len;
|
||||
|
||||
/* Read other fragments */
|
||||
amp_read_loc_assoc_frag(hdev, rp->phy_handle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(assoc->data + assoc->offset, rp->frag, rem_len);
|
||||
assoc->len = assoc->offset + rem_len;
|
||||
assoc->offset = 0;
|
||||
|
||||
send_rsp:
|
||||
/* Send A2MP Rsp when all fragments are received */
|
||||
a2mp_send_getampassoc_rsp(hdev, rp->status);
|
||||
a2mp_send_create_phy_link_req(hdev, rp->status);
|
||||
}
|
||||
|
||||
void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct amp_assoc *loc_assoc = &hdev->loc_assoc;
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("%s handle %d", hdev->name, phy_handle);
|
||||
|
||||
@ -231,12 +271,18 @@ void amp_read_loc_assoc_frag(struct hci_dev *hdev, u8 phy_handle)
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
cp.len_so_far = cpu_to_le16(loc_assoc->offset);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
err = hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
||||
void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
|
||||
memset(&hdev->loc_assoc, 0, sizeof(struct amp_assoc));
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
@ -244,7 +290,11 @@ void amp_read_loc_assoc(struct hci_dev *hdev, struct amp_mgr *mgr)
|
||||
cp.max_len = cpu_to_le16(hdev->amp_assoc_size);
|
||||
|
||||
set_bit(READ_LOC_AMP_ASSOC, &mgr->state);
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
||||
void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
@ -252,6 +302,8 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
{
|
||||
struct hci_cp_read_local_amp_assoc cp;
|
||||
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||
struct hci_request req;
|
||||
int err = 0;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
cp.len_so_far = cpu_to_le16(0);
|
||||
@ -260,7 +312,25 @@ void amp_read_loc_assoc_final_data(struct hci_dev *hdev,
|
||||
set_bit(READ_LOC_AMP_ASSOC_FINAL, &mgr->state);
|
||||
|
||||
/* Read Local AMP Assoc final link information data */
|
||||
hci_send_cmd(hdev, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_READ_LOCAL_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_run_skb(&req, read_local_amp_assoc_complete);
|
||||
if (err < 0)
|
||||
a2mp_send_getampassoc_rsp(hdev, A2MP_STATUS_INVALID_CTRL_ID);
|
||||
}
|
||||
|
||||
static void write_remote_amp_assoc_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_write_remote_amp_assoc *rp = (void *)skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x",
|
||||
hdev->name, rp->status, rp->phy_handle);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
amp_write_rem_assoc_continue(hdev, rp->phy_handle);
|
||||
}
|
||||
|
||||
/* Write AMP Assoc data fragments, returns true with last fragment written*/
|
||||
@ -270,6 +340,7 @@ static bool amp_write_rem_assoc_frag(struct hci_dev *hdev,
|
||||
struct hci_cp_write_remote_amp_assoc *cp;
|
||||
struct amp_mgr *mgr = hcon->amp_mgr;
|
||||
struct amp_ctrl *ctrl;
|
||||
struct hci_request req;
|
||||
u16 frag_len, len;
|
||||
|
||||
ctrl = amp_ctrl_lookup(mgr, hcon->remote_id);
|
||||
@ -307,7 +378,9 @@ static bool amp_write_rem_assoc_frag(struct hci_dev *hdev,
|
||||
|
||||
amp_ctrl_put(ctrl);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_WRITE_REMOTE_AMP_ASSOC, len, cp);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_WRITE_REMOTE_AMP_ASSOC, sizeof(cp), &cp);
|
||||
hci_req_run_skb(&req, write_remote_amp_assoc_complete);
|
||||
|
||||
kfree(cp);
|
||||
|
||||
@ -344,10 +417,37 @@ void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle)
|
||||
amp_write_rem_assoc_frag(hdev, hcon);
|
||||
}
|
||||
|
||||
static void create_phylink_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct hci_cp_create_phy_link *cp;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (status) {
|
||||
struct hci_conn *hcon;
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
|
||||
if (hcon)
|
||||
hci_conn_del(hcon);
|
||||
} else {
|
||||
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon)
|
||||
{
|
||||
struct hci_cp_create_phy_link cp;
|
||||
struct hci_request req;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
|
||||
@ -360,13 +460,33 @@ void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
return;
|
||||
}
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_CREATE_PHY_LINK, sizeof(cp), &cp);
|
||||
hci_req_run(&req, create_phylink_complete);
|
||||
}
|
||||
|
||||
static void accept_phylink_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct hci_cp_accept_phy_link *cp;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||
}
|
||||
|
||||
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon)
|
||||
{
|
||||
struct hci_cp_accept_phy_link cp;
|
||||
struct hci_request req;
|
||||
|
||||
cp.phy_handle = hcon->handle;
|
||||
|
||||
@ -379,7 +499,9 @@ void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
return;
|
||||
}
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
|
||||
hci_req_init(&req, hdev);
|
||||
hci_req_add(&req, HCI_OP_ACCEPT_PHY_LINK, sizeof(cp), &cp);
|
||||
hci_req_run(&req, accept_phylink_complete);
|
||||
}
|
||||
|
||||
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon)
|
||||
|
@ -44,6 +44,20 @@ void amp_create_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon);
|
||||
void amp_accept_phylink(struct hci_dev *hdev, struct amp_mgr *mgr,
|
||||
struct hci_conn *hcon);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
void amp_create_logical_link(struct l2cap_chan *chan);
|
||||
void amp_disconnect_logical_link(struct hci_chan *hchan);
|
||||
#else
|
||||
static inline void amp_create_logical_link(struct l2cap_chan *chan)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void amp_disconnect_logical_link(struct hci_chan *hchan)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
void amp_write_remote_assoc(struct hci_dev *hdev, u8 handle);
|
||||
void amp_write_rem_assoc_continue(struct hci_dev *hdev, u8 handle);
|
||||
void amp_physical_cfm(struct hci_conn *bredr_hcon, struct hci_conn *hs_hcon);
|
||||
|
@ -100,9 +100,9 @@ static void cmtp_application_del(struct cmtp_session *session, struct cmtp_appli
|
||||
static struct cmtp_application *cmtp_application_get(struct cmtp_session *session, int pattern, __u16 value)
|
||||
{
|
||||
struct cmtp_application *app;
|
||||
struct list_head *p, *n;
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each_safe(p, n, &session->applications) {
|
||||
list_for_each(p, &session->applications) {
|
||||
app = list_entry(p, struct cmtp_application, list);
|
||||
switch (pattern) {
|
||||
case CMTP_MSGNUM:
|
||||
@ -511,13 +511,13 @@ static int cmtp_proc_show(struct seq_file *m, void *v)
|
||||
struct capi_ctr *ctrl = m->private;
|
||||
struct cmtp_session *session = ctrl->driverdata;
|
||||
struct cmtp_application *app;
|
||||
struct list_head *p, *n;
|
||||
struct list_head *p;
|
||||
|
||||
seq_printf(m, "%s\n\n", cmtp_procinfo(ctrl));
|
||||
seq_printf(m, "addr %s\n", session->name);
|
||||
seq_printf(m, "ctrl %d\n", session->num);
|
||||
|
||||
list_for_each_safe(p, n, &session->applications) {
|
||||
list_for_each(p, &session->applications) {
|
||||
app = list_entry(p, struct cmtp_application, list);
|
||||
seq_printf(m, "appl %d -> %d\n", app->appl, app->mapping);
|
||||
}
|
||||
|
@ -2822,10 +2822,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
|
||||
{
|
||||
struct hci_conn_params *params;
|
||||
|
||||
/* The conn params list only contains identity addresses */
|
||||
if (!hci_is_identity_address(addr, addr_type))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(params, &hdev->le_conn_params, list) {
|
||||
if (bacmp(¶ms->addr, addr) == 0 &&
|
||||
params->addr_type == addr_type) {
|
||||
@ -2842,10 +2838,6 @@ struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
|
||||
{
|
||||
struct hci_conn_params *param;
|
||||
|
||||
/* The list only contains identity addresses */
|
||||
if (!hci_is_identity_address(addr, addr_type))
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry(param, list, action) {
|
||||
if (bacmp(¶m->addr, addr) == 0 &&
|
||||
param->addr_type == addr_type)
|
||||
@ -2861,9 +2853,6 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
|
||||
{
|
||||
struct hci_conn_params *params;
|
||||
|
||||
if (!hci_is_identity_address(addr, addr_type))
|
||||
return NULL;
|
||||
|
||||
params = hci_conn_params_lookup(hdev, addr, addr_type);
|
||||
if (params)
|
||||
return params;
|
||||
|
@ -823,7 +823,7 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
goto a2mp_rsp;
|
||||
return;
|
||||
|
||||
hdev->amp_status = rp->amp_status;
|
||||
hdev->amp_total_bw = __le32_to_cpu(rp->total_bw);
|
||||
@ -835,46 +835,6 @@ static void hci_cc_read_local_amp_info(struct hci_dev *hdev,
|
||||
hdev->amp_assoc_size = __le16_to_cpu(rp->max_assoc_size);
|
||||
hdev->amp_be_flush_to = __le32_to_cpu(rp->be_flush_to);
|
||||
hdev->amp_max_flush_to = __le32_to_cpu(rp->max_flush_to);
|
||||
|
||||
a2mp_rsp:
|
||||
a2mp_send_getinfo_rsp(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_read_local_amp_assoc(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_local_amp_assoc *rp = (void *) skb->data;
|
||||
struct amp_assoc *assoc = &hdev->loc_assoc;
|
||||
size_t rem_len, frag_len;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
goto a2mp_rsp;
|
||||
|
||||
frag_len = skb->len - sizeof(*rp);
|
||||
rem_len = __le16_to_cpu(rp->rem_len);
|
||||
|
||||
if (rem_len > frag_len) {
|
||||
BT_DBG("frag_len %zu rem_len %zu", frag_len, rem_len);
|
||||
|
||||
memcpy(assoc->data + assoc->offset, rp->frag, frag_len);
|
||||
assoc->offset += frag_len;
|
||||
|
||||
/* Read other fragments */
|
||||
amp_read_loc_assoc_frag(hdev, rp->phy_handle);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(assoc->data + assoc->offset, rp->frag, rem_len);
|
||||
assoc->len = assoc->offset + rem_len;
|
||||
assoc->offset = 0;
|
||||
|
||||
a2mp_rsp:
|
||||
/* Send A2MP Rsp when all fragments are received */
|
||||
a2mp_send_getampassoc_rsp(hdev, rp->status);
|
||||
a2mp_send_create_phy_link_req(hdev, rp->status);
|
||||
}
|
||||
|
||||
static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev,
|
||||
@ -1409,20 +1369,6 @@ static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cc_write_remote_amp_assoc(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_write_remote_amp_assoc *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x phy_handle 0x%2.2x",
|
||||
hdev->name, rp->status, rp->phy_handle);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
amp_write_rem_assoc_continue(hdev, rp->phy_handle);
|
||||
}
|
||||
|
||||
static void hci_cc_read_rssi(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_read_rssi *rp = (void *) skb->data;
|
||||
@ -1944,47 +1890,6 @@ static void hci_cs_disconnect(struct hci_dev *hdev, u8 status)
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_create_phylink(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_create_phy_link *cp;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_CREATE_PHY_LINK);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (status) {
|
||||
struct hci_conn *hcon;
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, cp->phy_handle);
|
||||
if (hcon)
|
||||
hci_conn_del(hcon);
|
||||
} else {
|
||||
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||
}
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_cs_accept_phylink(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_accept_phy_link *cp;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
cp = hci_sent_cmd_data(hdev, HCI_OP_ACCEPT_PHY_LINK);
|
||||
if (!cp)
|
||||
return;
|
||||
|
||||
amp_write_remote_assoc(hdev, cp->phy_handle);
|
||||
}
|
||||
|
||||
static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct hci_cp_le_create_conn *cp;
|
||||
@ -2998,10 +2903,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
hci_cc_read_clock(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_LOCAL_AMP_ASSOC:
|
||||
hci_cc_read_local_amp_assoc(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_INQ_RSP_TX_POWER:
|
||||
hci_cc_read_inq_rsp_tx_power(hdev, skb);
|
||||
break;
|
||||
@ -3106,10 +3007,6 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
hci_cc_set_adv_param(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_WRITE_REMOTE_AMP_ASSOC:
|
||||
hci_cc_write_remote_amp_assoc(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_READ_RSSI:
|
||||
hci_cc_read_rssi(hdev, skb);
|
||||
break;
|
||||
@ -3193,14 +3090,6 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb,
|
||||
hci_cs_setup_sync_conn(hdev, ev->status);
|
||||
break;
|
||||
|
||||
case HCI_OP_CREATE_PHY_LINK:
|
||||
hci_cs_create_phylink(hdev, ev->status);
|
||||
break;
|
||||
|
||||
case HCI_OP_ACCEPT_PHY_LINK:
|
||||
hci_cs_accept_phylink(hdev, ev->status);
|
||||
break;
|
||||
|
||||
case HCI_OP_SNIFF_MODE:
|
||||
hci_cs_sniff_mode(hdev, ev->status);
|
||||
break;
|
||||
@ -4399,6 +4288,23 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_channel_selected *ev = (void *)skb->data;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle);
|
||||
|
||||
skb_pull(skb, sizeof(*ev));
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (!hcon)
|
||||
return;
|
||||
|
||||
amp_read_loc_assoc_final_data(hdev, hcon);
|
||||
}
|
||||
|
||||
static void hci_phy_link_complete_evt(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
@ -4522,6 +4428,7 @@ static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev,
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
@ -5206,22 +5113,6 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
static void hci_chan_selected_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_channel_selected *ev = (void *) skb->data;
|
||||
struct hci_conn *hcon;
|
||||
|
||||
BT_DBG("%s handle 0x%2.2x", hdev->name, ev->phy_handle);
|
||||
|
||||
skb_pull(skb, sizeof(*ev));
|
||||
|
||||
hcon = hci_conn_hash_lookup_handle(hdev, ev->phy_handle);
|
||||
if (!hcon)
|
||||
return;
|
||||
|
||||
amp_read_loc_assoc_final_data(hdev, hcon);
|
||||
}
|
||||
|
||||
static bool hci_get_cmd_complete(struct hci_dev *hdev, u16 opcode,
|
||||
u8 event, struct sk_buff *skb)
|
||||
{
|
||||
@ -5442,14 +5333,15 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
hci_le_meta_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_CHANNEL_SELECTED:
|
||||
hci_chan_selected_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_REMOTE_OOB_DATA_REQUEST:
|
||||
hci_remote_oob_data_request_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_HS)
|
||||
case HCI_EV_CHANNEL_SELECTED:
|
||||
hci_chan_selected_evt(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_EV_PHY_LINK_COMPLETE:
|
||||
hci_phy_link_complete_evt(hdev, skb);
|
||||
break;
|
||||
@ -5465,6 +5357,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
case HCI_EV_DISCONN_PHY_LINK_COMPLETE:
|
||||
hci_disconn_phylink_complete_evt(hdev, skb);
|
||||
break;
|
||||
#endif
|
||||
|
||||
case HCI_EV_NUM_COMP_BLOCKS:
|
||||
hci_num_comp_blocks_evt(hdev, skb);
|
||||
|
@ -1054,18 +1054,23 @@ static void l2cap_sock_kill(struct sock *sk)
|
||||
sock_put(sk);
|
||||
}
|
||||
|
||||
static int __l2cap_wait_ack(struct sock *sk)
|
||||
static int __l2cap_wait_ack(struct sock *sk, struct l2cap_chan *chan)
|
||||
{
|
||||
struct l2cap_chan *chan = l2cap_pi(sk)->chan;
|
||||
DECLARE_WAITQUEUE(wait, current);
|
||||
int err = 0;
|
||||
int timeo = HZ/5;
|
||||
int timeo = L2CAP_WAIT_ACK_POLL_PERIOD;
|
||||
/* Timeout to prevent infinite loop */
|
||||
unsigned long timeout = jiffies + L2CAP_WAIT_ACK_TIMEOUT;
|
||||
|
||||
add_wait_queue(sk_sleep(sk), &wait);
|
||||
set_current_state(TASK_INTERRUPTIBLE);
|
||||
while (chan->unacked_frames > 0 && chan->conn) {
|
||||
do {
|
||||
BT_DBG("Waiting for %d ACKs, timeout %04d ms",
|
||||
chan->unacked_frames, time_after(jiffies, timeout) ? 0 :
|
||||
jiffies_to_msecs(timeout - jiffies));
|
||||
|
||||
if (!timeo)
|
||||
timeo = HZ/5;
|
||||
timeo = L2CAP_WAIT_ACK_POLL_PERIOD;
|
||||
|
||||
if (signal_pending(current)) {
|
||||
err = sock_intr_errno(timeo);
|
||||
@ -1080,7 +1085,15 @@ static int __l2cap_wait_ack(struct sock *sk)
|
||||
err = sock_error(sk);
|
||||
if (err)
|
||||
break;
|
||||
}
|
||||
|
||||
if (time_after(jiffies, timeout)) {
|
||||
err = -ENOLINK;
|
||||
break;
|
||||
}
|
||||
|
||||
} while (chan->unacked_frames > 0 &&
|
||||
chan->state == BT_CONNECTED);
|
||||
|
||||
set_current_state(TASK_RUNNING);
|
||||
remove_wait_queue(sk_sleep(sk), &wait);
|
||||
return err;
|
||||
@ -1098,7 +1111,12 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
if (!sk)
|
||||
return 0;
|
||||
|
||||
/* prevent sk structure from being freed whilst unlocked */
|
||||
sock_hold(sk);
|
||||
|
||||
chan = l2cap_pi(sk)->chan;
|
||||
/* prevent chan structure from being freed whilst unlocked */
|
||||
l2cap_chan_hold(chan);
|
||||
conn = chan->conn;
|
||||
|
||||
BT_DBG("chan %p state %s", chan, state_to_string(chan->state));
|
||||
@ -1110,8 +1128,10 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
lock_sock(sk);
|
||||
|
||||
if (!sk->sk_shutdown) {
|
||||
if (chan->mode == L2CAP_MODE_ERTM)
|
||||
err = __l2cap_wait_ack(sk);
|
||||
if (chan->mode == L2CAP_MODE_ERTM &&
|
||||
chan->unacked_frames > 0 &&
|
||||
chan->state == BT_CONNECTED)
|
||||
err = __l2cap_wait_ack(sk, chan);
|
||||
|
||||
sk->sk_shutdown = SHUTDOWN_MASK;
|
||||
|
||||
@ -1134,6 +1154,11 @@ static int l2cap_sock_shutdown(struct socket *sock, int how)
|
||||
if (conn)
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
|
||||
l2cap_chan_put(chan);
|
||||
sock_put(sk);
|
||||
|
||||
BT_DBG("err: %d", err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -6226,6 +6226,17 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
|
||||
else
|
||||
auto_conn = HCI_AUTO_CONN_REPORT;
|
||||
|
||||
/* Kernel internally uses conn_params with resolvable private
|
||||
* address, but Add Device allows only identity addresses.
|
||||
* Make sure it is enforced before calling
|
||||
* hci_conn_params_lookup.
|
||||
*/
|
||||
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
|
||||
err = cmd->cmd_complete(cmd, MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* If the connection parameters don't exist for this device,
|
||||
* they will be created and configured with defaults.
|
||||
*/
|
||||
@ -6340,6 +6351,18 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
||||
else
|
||||
addr_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
/* Kernel internally uses conn_params with resolvable private
|
||||
* address, but Remove Device allows only identity addresses.
|
||||
* Make sure it is enforced before calling
|
||||
* hci_conn_params_lookup.
|
||||
*/
|
||||
if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) {
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
|
||||
addr_type);
|
||||
if (!params) {
|
||||
|
@ -23,6 +23,26 @@ rdev_del_virtual_intf_deprecated(struct cfg802154_registered_device *rdev,
|
||||
rdev->ops->del_virtual_intf_deprecated(&rdev->wpan_phy, dev);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_suspend(struct cfg802154_registered_device *rdev)
|
||||
{
|
||||
int ret;
|
||||
trace_802154_rdev_suspend(&rdev->wpan_phy);
|
||||
ret = rdev->ops->suspend(&rdev->wpan_phy);
|
||||
trace_802154_rdev_return_int(&rdev->wpan_phy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_resume(struct cfg802154_registered_device *rdev)
|
||||
{
|
||||
int ret;
|
||||
trace_802154_rdev_resume(&rdev->wpan_phy);
|
||||
ret = rdev->ops->resume(&rdev->wpan_phy);
|
||||
trace_802154_rdev_return_int(&rdev->wpan_phy, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_add_virtual_intf(struct cfg802154_registered_device *rdev, char *name,
|
||||
unsigned char name_assign_type,
|
||||
|
@ -14,11 +14,13 @@
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
|
||||
#include <net/cfg802154.h>
|
||||
|
||||
#include "core.h"
|
||||
#include "sysfs.h"
|
||||
#include "rdev-ops.h"
|
||||
|
||||
static inline struct cfg802154_registered_device *
|
||||
dev_to_rdev(struct device *dev)
|
||||
@ -62,10 +64,46 @@ static struct attribute *pmib_attrs[] = {
|
||||
};
|
||||
ATTRIBUTE_GROUPS(pmib);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int wpan_phy_suspend(struct device *dev)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (rdev->ops->suspend) {
|
||||
rtnl_lock();
|
||||
ret = rdev_suspend(rdev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int wpan_phy_resume(struct device *dev)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = dev_to_rdev(dev);
|
||||
int ret = 0;
|
||||
|
||||
if (rdev->ops->resume) {
|
||||
rtnl_lock();
|
||||
ret = rdev_resume(rdev);
|
||||
rtnl_unlock();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(wpan_phy_pm_ops, wpan_phy_suspend, wpan_phy_resume);
|
||||
#define WPAN_PHY_PM_OPS (&wpan_phy_pm_ops)
|
||||
#else
|
||||
#define WPAN_PHY_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
struct class wpan_phy_class = {
|
||||
.name = "ieee802154",
|
||||
.dev_release = wpan_phy_release,
|
||||
.dev_groups = pmib_groups,
|
||||
.pm = WPAN_PHY_PM_OPS,
|
||||
};
|
||||
|
||||
int wpan_phy_sysfs_init(void)
|
||||
|
@ -40,6 +40,28 @@
|
||||
* rdev->ops traces *
|
||||
*************************************************************/
|
||||
|
||||
DECLARE_EVENT_CLASS(wpan_phy_only_evt,
|
||||
TP_PROTO(struct wpan_phy *wpan_phy),
|
||||
TP_ARGS(wpan_phy),
|
||||
TP_STRUCT__entry(
|
||||
WPAN_PHY_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
WPAN_PHY_ASSIGN;
|
||||
),
|
||||
TP_printk(WPAN_PHY_PR_FMT, WPAN_PHY_PR_ARG)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wpan_phy_only_evt, 802154_rdev_suspend,
|
||||
TP_PROTO(struct wpan_phy *wpan_phy),
|
||||
TP_ARGS(wpan_phy)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(wpan_phy_only_evt, 802154_rdev_resume,
|
||||
TP_PROTO(struct wpan_phy *wpan_phy),
|
||||
TP_ARGS(wpan_phy)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_rdev_add_virtual_intf,
|
||||
TP_PROTO(struct wpan_phy *wpan_phy, char *name,
|
||||
enum nl802154_iftype type, __le64 extended_addr),
|
||||
|
@ -44,6 +44,49 @@ static void ieee802154_del_iface_deprecated(struct wpan_phy *wpan_phy,
|
||||
ieee802154_if_remove(sdata);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ieee802154_suspend(struct wpan_phy *wpan_phy)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
|
||||
if (!local->open_count)
|
||||
goto suspend;
|
||||
|
||||
ieee802154_stop_queue(&local->hw);
|
||||
synchronize_net();
|
||||
|
||||
/* stop hardware - this must stop RX */
|
||||
ieee802154_stop_device(local);
|
||||
|
||||
suspend:
|
||||
local->suspended = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ieee802154_resume(struct wpan_phy *wpan_phy)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
int ret;
|
||||
|
||||
/* nothing to do if HW shouldn't run */
|
||||
if (!local->open_count)
|
||||
goto wake_up;
|
||||
|
||||
/* restart hardware */
|
||||
ret = drv_start(local);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
wake_up:
|
||||
ieee802154_wake_queue(&local->hw);
|
||||
local->suspended = false;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ieee802154_suspend NULL
|
||||
#define ieee802154_resume NULL
|
||||
#endif
|
||||
|
||||
static int
|
||||
ieee802154_add_iface(struct wpan_phy *phy, const char *name,
|
||||
unsigned char name_assign_type,
|
||||
@ -145,13 +188,18 @@ static int
|
||||
ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
__le16 pan_id)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (wpan_dev->pan_id == pan_id)
|
||||
return 0;
|
||||
|
||||
wpan_dev->pan_id = pan_id;
|
||||
return 0;
|
||||
ret = mac802154_wpan_update_llsec(wpan_dev->netdev);
|
||||
if (!ret)
|
||||
wpan_dev->pan_id = pan_id;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
@ -227,6 +275,8 @@ ieee802154_set_lbt_mode(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
const struct cfg802154_ops mac802154_config_ops = {
|
||||
.add_virtual_intf_deprecated = ieee802154_add_iface_deprecated,
|
||||
.del_virtual_intf_deprecated = ieee802154_del_iface_deprecated,
|
||||
.suspend = ieee802154_suspend,
|
||||
.resume = ieee802154_resume,
|
||||
.add_virtual_intf = ieee802154_add_iface,
|
||||
.del_virtual_intf = ieee802154_del_iface,
|
||||
.set_channel = ieee802154_set_channel,
|
||||
|
@ -56,9 +56,13 @@ struct ieee802154_local {
|
||||
struct hrtimer ifs_timer;
|
||||
|
||||
bool started;
|
||||
bool suspended;
|
||||
|
||||
struct tasklet_struct tasklet;
|
||||
struct sk_buff_head skb_queue;
|
||||
|
||||
struct sk_buff *tx_skb;
|
||||
struct work_struct tx_work;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -94,8 +98,6 @@ struct ieee802154_sub_if_data {
|
||||
struct mac802154_llsec sec;
|
||||
};
|
||||
|
||||
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
|
||||
|
||||
/* utility functions/constants */
|
||||
extern const void *const mac802154_wpan_phy_privid; /* for wpan_phy privid */
|
||||
|
||||
@ -125,6 +127,8 @@ ieee802154_sdata_running(struct ieee802154_sub_if_data *sdata)
|
||||
|
||||
extern struct ieee802154_mlme_ops mac802154_mlme_wpan;
|
||||
|
||||
void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb);
|
||||
void ieee802154_xmit_worker(struct work_struct *work);
|
||||
netdev_tx_t
|
||||
ieee802154_monitor_start_xmit(struct sk_buff *skb, struct net_device *dev);
|
||||
netdev_tx_t
|
||||
@ -167,6 +171,8 @@ void mac802154_get_table(struct net_device *dev,
|
||||
struct ieee802154_llsec_table **t);
|
||||
void mac802154_unlock_table(struct net_device *dev);
|
||||
|
||||
int mac802154_wpan_update_llsec(struct net_device *dev);
|
||||
|
||||
/* interface handling */
|
||||
int ieee802154_iface_init(void);
|
||||
void ieee802154_iface_exit(void);
|
||||
@ -176,5 +182,6 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
||||
unsigned char name_assign_type, enum nl802154_iftype type,
|
||||
__le64 extended_addr);
|
||||
void ieee802154_remove_interfaces(struct ieee802154_local *local);
|
||||
void ieee802154_stop_device(struct ieee802154_local *local);
|
||||
|
||||
#endif /* __IEEE802154_I_H */
|
||||
|
@ -30,7 +30,7 @@
|
||||
#include "ieee802154_i.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
static int mac802154_wpan_update_llsec(struct net_device *dev)
|
||||
int mac802154_wpan_update_llsec(struct net_device *dev)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_mlme_ops *ops = ieee802154_mlme_ops(dev);
|
||||
@ -314,11 +314,8 @@ static int mac802154_slave_close(struct net_device *dev)
|
||||
|
||||
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
||||
if (!local->open_count) {
|
||||
flush_workqueue(local->workqueue);
|
||||
hrtimer_cancel(&local->ifs_timer);
|
||||
drv_stop(local);
|
||||
}
|
||||
if (!local->open_count)
|
||||
ieee802154_stop_device(local);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -471,6 +468,7 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
|
||||
enum nl802154_iftype type)
|
||||
{
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
int ret;
|
||||
u8 tmp;
|
||||
|
||||
/* set some type-dependent values */
|
||||
@ -505,6 +503,10 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
|
||||
mutex_init(&sdata->sec_mtx);
|
||||
|
||||
mac802154_llsec_init(&sdata->sec);
|
||||
ret = mac802154_wpan_update_llsec(sdata->dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
case NL802154_IFTYPE_MONITOR:
|
||||
sdata->dev->destructor = free_netdev;
|
||||
|
@ -40,7 +40,7 @@ static void ieee802154_tasklet_handler(unsigned long data)
|
||||
* netstack.
|
||||
*/
|
||||
skb->pkt_type = 0;
|
||||
ieee802154_rx(&local->hw, skb);
|
||||
ieee802154_rx(local, skb);
|
||||
break;
|
||||
default:
|
||||
WARN(1, "mac802154: Packet is of unknown type %d\n",
|
||||
@ -58,11 +58,9 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
|
||||
struct ieee802154_local *local;
|
||||
size_t priv_size;
|
||||
|
||||
if (!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
|
||||
!ops->start || !ops->stop || !ops->set_channel) {
|
||||
pr_err("undefined IEEE802.15.4 device operations\n");
|
||||
if (WARN_ON(!ops || !(ops->xmit_async || ops->xmit_sync) || !ops->ed ||
|
||||
!ops->start || !ops->stop || !ops->set_channel))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Ensure 32-byte alignment of our private data and hw private data.
|
||||
* We use the wpan_phy priv data for both our ieee802154_local and for
|
||||
@ -107,6 +105,8 @@ ieee802154_alloc_hw(size_t priv_data_len, const struct ieee802154_ops *ops)
|
||||
|
||||
skb_queue_head_init(&local->skb_queue);
|
||||
|
||||
INIT_WORK(&local->tx_work, ieee802154_xmit_worker);
|
||||
|
||||
/* init supported flags with 802.15.4 default ranges */
|
||||
phy->supported.max_minbe = 8;
|
||||
phy->supported.min_maxbe = 3;
|
||||
|
@ -246,13 +246,15 @@ ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
|
||||
}
|
||||
}
|
||||
|
||||
void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
|
||||
void ieee802154_rx(struct ieee802154_local *local, struct sk_buff *skb)
|
||||
{
|
||||
struct ieee802154_local *local = hw_to_local(hw);
|
||||
u16 crc;
|
||||
|
||||
WARN_ON_ONCE(softirq_count() == 0);
|
||||
|
||||
if (local->suspended)
|
||||
goto drop;
|
||||
|
||||
/* TODO: When a transceiver omits the checksum here, we
|
||||
* add an own calculated one. This is currently an ugly
|
||||
* solution because the monitor needs a crc here.
|
||||
@ -273,8 +275,7 @@ void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
|
||||
crc = crc_ccitt(0, skb->data, skb->len);
|
||||
if (crc) {
|
||||
rcu_read_unlock();
|
||||
kfree_skb(skb);
|
||||
return;
|
||||
goto drop;
|
||||
}
|
||||
}
|
||||
/* remove crc */
|
||||
@ -283,8 +284,11 @@ void ieee802154_rx(struct ieee802154_hw *hw, struct sk_buff *skb)
|
||||
__ieee802154_rx_handle_packet(local, skb);
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
return;
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee802154_rx);
|
||||
|
||||
void
|
||||
ieee802154_rx_irqsafe(struct ieee802154_hw *hw, struct sk_buff *skb, u8 lqi)
|
||||
|
@ -30,23 +30,11 @@
|
||||
#include "ieee802154_i.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
/* IEEE 802.15.4 transceivers can sleep during the xmit session, so process
|
||||
* packets through the workqueue.
|
||||
*/
|
||||
struct ieee802154_xmit_cb {
|
||||
struct sk_buff *skb;
|
||||
struct work_struct work;
|
||||
struct ieee802154_local *local;
|
||||
};
|
||||
|
||||
static struct ieee802154_xmit_cb ieee802154_xmit_cb;
|
||||
|
||||
static void ieee802154_xmit_worker(struct work_struct *work)
|
||||
void ieee802154_xmit_worker(struct work_struct *work)
|
||||
{
|
||||
struct ieee802154_xmit_cb *cb =
|
||||
container_of(work, struct ieee802154_xmit_cb, work);
|
||||
struct ieee802154_local *local = cb->local;
|
||||
struct sk_buff *skb = cb->skb;
|
||||
struct ieee802154_local *local =
|
||||
container_of(work, struct ieee802154_local, tx_work);
|
||||
struct sk_buff *skb = local->tx_skb;
|
||||
struct net_device *dev = skb->dev;
|
||||
int res;
|
||||
|
||||
@ -106,11 +94,8 @@ ieee802154_tx(struct ieee802154_local *local, struct sk_buff *skb)
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len;
|
||||
} else {
|
||||
INIT_WORK(&ieee802154_xmit_cb.work, ieee802154_xmit_worker);
|
||||
ieee802154_xmit_cb.skb = skb;
|
||||
ieee802154_xmit_cb.local = local;
|
||||
|
||||
queue_work(local->workqueue, &ieee802154_xmit_cb.work);
|
||||
local->tx_skb = skb;
|
||||
queue_work(local->workqueue, &local->tx_work);
|
||||
}
|
||||
|
||||
return NETDEV_TX_OK;
|
||||
|
@ -14,6 +14,7 @@
|
||||
*/
|
||||
|
||||
#include "ieee802154_i.h"
|
||||
#include "driver-ops.h"
|
||||
|
||||
/* privid for wpan_phys to determine whether they belong to us or not */
|
||||
const void *const mac802154_wpan_phy_privid = &mac802154_wpan_phy_privid;
|
||||
@ -92,3 +93,10 @@ void ieee802154_xmit_complete(struct ieee802154_hw *hw, struct sk_buff *skb,
|
||||
dev_consume_skb_any(skb);
|
||||
}
|
||||
EXPORT_SYMBOL(ieee802154_xmit_complete);
|
||||
|
||||
void ieee802154_stop_device(struct ieee802154_local *local)
|
||||
{
|
||||
flush_workqueue(local->workqueue);
|
||||
hrtimer_cancel(&local->ifs_timer);
|
||||
drv_stop(local);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user