mirror of
https://github.com/torvalds/linux.git
synced 2024-10-30 16:51:45 +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 2015-06-18 Here's the final bluetooth-next pull request for 4.2. - Cleanups & fixes to 802.15.4 code and related drivers - Fix btusb driver memory leak - New USB IDs for Atheros controllers - Support for BCM4324B3 UART based Broadcom controller - Fix for Bluetooth encryption key size handling - Broadcom controller initialization fixes - Support for Intel controller DDC parameters - Support for multiple Bluetooth LE advertising instances - Fix for HCI user channel cleanup path 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
bfdc8dbdf8
@ -1875,6 +1875,14 @@ W: http://www.attotech.com
|
||||
S: Supported
|
||||
F: drivers/scsi/esas2r
|
||||
|
||||
ATUSB IEEE 802.15.4 RADIO DRIVER
|
||||
M: Stefan Schmidt <stefan@osg.samsung.com>
|
||||
L: linux-wpan@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/net/ieee802154/atusb.c
|
||||
F: drivers/net/ieee802154/atusb.h
|
||||
F: drivers/net/ieee802154/at86rf230.h
|
||||
|
||||
AUDIT SUBSYSTEM
|
||||
M: Paul Moore <paul@paul-moore.com>
|
||||
M: Eric Paris <eparis@redhat.com>
|
||||
|
@ -80,6 +80,7 @@ static const struct usb_device_id ath3k_table[] = {
|
||||
{ USB_DEVICE(0x0489, 0xe057) },
|
||||
{ USB_DEVICE(0x0489, 0xe056) },
|
||||
{ USB_DEVICE(0x0489, 0xe05f) },
|
||||
{ USB_DEVICE(0x0489, 0xe076) },
|
||||
{ USB_DEVICE(0x0489, 0xe078) },
|
||||
{ USB_DEVICE(0x04c5, 0x1330) },
|
||||
{ USB_DEVICE(0x04CA, 0x3004) },
|
||||
@ -88,6 +89,7 @@ static const struct usb_device_id ath3k_table[] = {
|
||||
{ USB_DEVICE(0x04CA, 0x3007) },
|
||||
{ USB_DEVICE(0x04CA, 0x3008) },
|
||||
{ USB_DEVICE(0x04CA, 0x300b) },
|
||||
{ USB_DEVICE(0x04CA, 0x300d) },
|
||||
{ USB_DEVICE(0x04CA, 0x300f) },
|
||||
{ USB_DEVICE(0x04CA, 0x3010) },
|
||||
{ USB_DEVICE(0x0930, 0x0219) },
|
||||
@ -113,6 +115,7 @@ static const struct usb_device_id ath3k_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3408) },
|
||||
{ USB_DEVICE(0x13d3, 0x3423) },
|
||||
{ USB_DEVICE(0x13d3, 0x3432) },
|
||||
{ USB_DEVICE(0x13d3, 0x3474) },
|
||||
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE02C) },
|
||||
@ -137,6 +140,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
||||
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
||||
{ 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(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
|
||||
@ -145,6 +149,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
||||
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
|
||||
{ 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(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||
@ -170,6 +175,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Atheros AR5BBU22 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 },
|
||||
|
@ -202,9 +202,8 @@ static void bt3c_write_wakeup(struct bt3c_info *info)
|
||||
/* Send frame */
|
||||
len = bt3c_write(iobase, 256, skb->data, skb->len);
|
||||
|
||||
if (len != skb->len) {
|
||||
if (len != skb->len)
|
||||
BT_ERR("Very strange");
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
|
@ -33,6 +33,7 @@
|
||||
#define VERSION "0.1"
|
||||
|
||||
#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}})
|
||||
#define BDADDR_BCM4324B3 (&(bdaddr_t) {{0x00, 0x00, 0x00, 0xb3, 0x24, 0x43}})
|
||||
|
||||
int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
{
|
||||
@ -56,10 +57,18 @@ int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
|
||||
bda = (struct hci_rp_read_bd_addr *)skb->data;
|
||||
|
||||
/* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
|
||||
/* Check if the address indicates a controller with either an
|
||||
* invalid or default address. In both cases the device needs
|
||||
* to be marked as not having a valid address.
|
||||
*
|
||||
* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller
|
||||
* with no configured address.
|
||||
*
|
||||
* The address 43:24:B3:00:00:00 indicates a BCM4324B3 controller
|
||||
* with waiting for configuration state.
|
||||
*/
|
||||
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) {
|
||||
if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0) ||
|
||||
!bacmp(&bda->bdaddr, BDADDR_BCM4324B3)) {
|
||||
BT_INFO("%s: BCM: Using default device address (%pMR)",
|
||||
hdev->name, &bda->bdaddr);
|
||||
set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks);
|
||||
@ -89,21 +98,14 @@ int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btbcm_set_bdaddr);
|
||||
|
||||
int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
|
||||
int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
|
||||
{
|
||||
const struct hci_command_hdr *cmd;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
size_t fw_size;
|
||||
struct sk_buff *skb;
|
||||
u16 opcode;
|
||||
int err;
|
||||
|
||||
err = request_firmware(&fw, firmware, &hdev->dev);
|
||||
if (err < 0) {
|
||||
BT_INFO("%s: BCM: Patch %s not found", hdev->name, firmware);
|
||||
return err;
|
||||
}
|
||||
int err = 0;
|
||||
|
||||
/* Start Download */
|
||||
skb = __hci_cmd_sync(hdev, 0xfc2e, 0, NULL, HCI_INIT_TIMEOUT);
|
||||
@ -129,8 +131,7 @@ int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
|
||||
fw_size -= sizeof(*cmd);
|
||||
|
||||
if (fw_size < cmd->plen) {
|
||||
BT_ERR("%s: BCM: Patch %s is corrupted", hdev->name,
|
||||
firmware);
|
||||
BT_ERR("%s: BCM: Patch is corrupted", hdev->name);
|
||||
err = -EINVAL;
|
||||
goto done;
|
||||
}
|
||||
@ -156,7 +157,6 @@ int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
|
||||
msleep(250);
|
||||
|
||||
done:
|
||||
release_firmware(fw);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL(btbcm_patchram);
|
||||
@ -242,9 +242,101 @@ static const struct {
|
||||
const char *name;
|
||||
} bcm_uart_subver_table[] = {
|
||||
{ 0x410e, "BCM43341B0" }, /* 002.001.014 */
|
||||
{ 0x4406, "BCM4324B3" }, /* 002.004.006 */
|
||||
{ 0x610c, "BCM4354" }, /* 003.001.012 */
|
||||
{ }
|
||||
};
|
||||
|
||||
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len)
|
||||
{
|
||||
u16 subver, rev;
|
||||
const char *hw_name = NULL;
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *ver;
|
||||
int i, err;
|
||||
|
||||
/* Reset */
|
||||
err = btbcm_reset(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Read Local Version Info */
|
||||
skb = btbcm_read_local_version(hdev);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
rev = le16_to_cpu(ver->hci_rev);
|
||||
subver = le16_to_cpu(ver->lmp_subver);
|
||||
kfree_skb(skb);
|
||||
|
||||
/* Read Verbose Config Version Info */
|
||||
skb = btbcm_read_verbose_config(hdev);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
BT_INFO("%s: BCM: chip id %u", hdev->name, skb->data[1]);
|
||||
kfree_skb(skb);
|
||||
|
||||
switch ((rev & 0xf000) >> 12) {
|
||||
case 0:
|
||||
case 1:
|
||||
case 3:
|
||||
for (i = 0; bcm_uart_subver_table[i].name; i++) {
|
||||
if (subver == bcm_uart_subver_table[i].subver) {
|
||||
hw_name = bcm_uart_subver_table[i].name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(fw_name, len, "brcm/%s.hcd", hw_name ? : "BCM");
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
BT_INFO("%s: %s (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
||||
hw_name ? : "BCM", (subver & 0x7000) >> 13,
|
||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btbcm_initialize);
|
||||
|
||||
int btbcm_finalize(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
struct hci_rp_read_local_version *ver;
|
||||
u16 subver, rev;
|
||||
int err;
|
||||
|
||||
/* Reset */
|
||||
err = btbcm_reset(hdev);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Read Local Version Info */
|
||||
skb = btbcm_read_local_version(hdev);
|
||||
if (IS_ERR(skb))
|
||||
return PTR_ERR(skb);
|
||||
|
||||
ver = (struct hci_rp_read_local_version *)skb->data;
|
||||
rev = le16_to_cpu(ver->hci_rev);
|
||||
subver = le16_to_cpu(ver->lmp_subver);
|
||||
kfree_skb(skb);
|
||||
|
||||
BT_INFO("%s: BCM (%3.3u.%3.3u.%3.3u) build %4.4u", hdev->name,
|
||||
(subver & 0x7000) >> 13, (subver & 0x1f00) >> 8,
|
||||
(subver & 0x00ff), rev & 0x0fff);
|
||||
|
||||
btbcm_check_bdaddr(hdev);
|
||||
|
||||
set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(btbcm_finalize);
|
||||
|
||||
static const struct {
|
||||
u16 subver;
|
||||
const char *name;
|
||||
@ -265,6 +357,7 @@ static const struct {
|
||||
int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||
{
|
||||
char fw_name[64];
|
||||
const struct firmware *fw;
|
||||
u16 subver, rev, pid, vid;
|
||||
const char *hw_name = NULL;
|
||||
struct sk_buff *skb;
|
||||
@ -296,6 +389,7 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||
|
||||
switch ((rev & 0xf000) >> 12) {
|
||||
case 0:
|
||||
case 3:
|
||||
for (i = 0; bcm_uart_subver_table[i].name; i++) {
|
||||
if (subver == bcm_uart_subver_table[i].subver) {
|
||||
hw_name = bcm_uart_subver_table[i].name;
|
||||
@ -335,9 +429,15 @@ int btbcm_setup_patchram(struct hci_dev *hdev)
|
||||
hw_name ? : "BCM", (subver & 0x7000) >> 13,
|
||||
(subver & 0x1f00) >> 8, (subver & 0x00ff), rev & 0x0fff);
|
||||
|
||||
err = btbcm_patchram(hdev, fw_name);
|
||||
if (err == -ENOENT)
|
||||
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;
|
||||
}
|
||||
|
||||
btbcm_patchram(hdev, fw);
|
||||
|
||||
release_firmware(fw);
|
||||
|
||||
/* Reset */
|
||||
err = btbcm_reset(hdev);
|
||||
|
@ -21,15 +21,61 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#define BCM_UART_CLOCK_48MHZ 0x01
|
||||
#define BCM_UART_CLOCK_24MHZ 0x02
|
||||
|
||||
struct bcm_update_uart_baud_rate {
|
||||
__le16 zero;
|
||||
__le32 baud_rate;
|
||||
} __packed;
|
||||
|
||||
struct bcm_write_uart_clock_setting {
|
||||
__u8 type;
|
||||
} __packed;
|
||||
|
||||
struct bcm_set_sleep_mode {
|
||||
__u8 sleep_mode;
|
||||
__u8 idle_host;
|
||||
__u8 idle_dev;
|
||||
__u8 bt_wake_active;
|
||||
__u8 host_wake_active;
|
||||
__u8 allow_host_sleep;
|
||||
__u8 combine_modes;
|
||||
__u8 tristate_control;
|
||||
__u8 usb_auto_sleep;
|
||||
__u8 usb_resume_timeout;
|
||||
__u8 pulsed_host_wake;
|
||||
__u8 break_to_host;
|
||||
} __packed;
|
||||
|
||||
struct bcm_set_pcm_int_params {
|
||||
__u8 routing;
|
||||
__u8 rate;
|
||||
__u8 frame_sync;
|
||||
__u8 sync_mode;
|
||||
__u8 clock_mode;
|
||||
} __packed;
|
||||
|
||||
struct bcm_set_pcm_format_params {
|
||||
__u8 lsb_first;
|
||||
__u8 fill_value;
|
||||
__u8 fill_method;
|
||||
__u8 fill_num;
|
||||
__u8 right_justify;
|
||||
} __packed;
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_BCM)
|
||||
|
||||
int btbcm_check_bdaddr(struct hci_dev *hdev);
|
||||
int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
|
||||
int btbcm_patchram(struct hci_dev *hdev, const char *firmware);
|
||||
int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw);
|
||||
|
||||
int btbcm_setup_patchram(struct hci_dev *hdev);
|
||||
int btbcm_setup_apple(struct hci_dev *hdev);
|
||||
|
||||
int btbcm_initialize(struct hci_dev *hdev, char *fw_name, size_t len);
|
||||
int btbcm_finalize(struct hci_dev *hdev);
|
||||
|
||||
#else
|
||||
|
||||
static inline int btbcm_check_bdaddr(struct hci_dev *hdev)
|
||||
@ -42,7 +88,7 @@ static inline int btbcm_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static inline int btbcm_patchram(struct hci_dev *hdev, const char *firmware)
|
||||
static inline int btbcm_patchram(struct hci_dev *hdev, const struct firmware *fw)
|
||||
{
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@ -57,4 +103,15 @@ static inline int btbcm_setup_apple(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int btbcm_initialize(struct hci_dev *hdev, char *fw_name,
|
||||
size_t len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int btbcm_finalize(struct hci_dev *hdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -179,6 +179,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0489, 0xe056), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x0489, 0xe057), .driver_info = BTUSB_ATH3012 },
|
||||
{ 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(0x04c5, 0x1330), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x04ca, 0x3004), .driver_info = BTUSB_ATH3012 },
|
||||
@ -187,6 +188,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x04ca, 0x3007), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x04ca, 0x3008), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x04ca, 0x300b), .driver_info = BTUSB_ATH3012 },
|
||||
{ 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(0x0930, 0x0219), .driver_info = BTUSB_ATH3012 },
|
||||
@ -212,6 +214,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x13d3, 0x3408), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x13d3, 0x3423), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 },
|
||||
{ USB_DEVICE(0x13d3, 0x3474), .driver_info = BTUSB_ATH3012 },
|
||||
|
||||
/* Atheros AR5BBU12 with sflash firmware */
|
||||
{ USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE },
|
||||
@ -266,7 +269,7 @@ static const struct usb_device_id blacklist_table[] = {
|
||||
{ USB_DEVICE(0x0e5e, 0x6622), .driver_info = BTUSB_BROKEN_ISOC },
|
||||
|
||||
/* Roper Class 1 Bluetooth Dongle (Silicon Wave based) */
|
||||
{ USB_DEVICE(0x1300, 0x0001), .driver_info = BTUSB_SWAVE },
|
||||
{ USB_DEVICE(0x1310, 0x0001), .driver_info = BTUSB_SWAVE },
|
||||
|
||||
/* Digianswer devices */
|
||||
{ USB_DEVICE(0x08fd, 0x0001), .driver_info = BTUSB_DIGIANSWER },
|
||||
@ -1300,28 +1303,6 @@ static void btusb_waker(struct work_struct *work)
|
||||
usb_autopm_put_interface(data->intf);
|
||||
}
|
||||
|
||||
static struct sk_buff *btusb_read_local_version(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION failed (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
return skb;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
|
||||
BT_ERR("%s: HCI_OP_READ_LOCAL_VERSION event length mismatch",
|
||||
hdev->name);
|
||||
kfree_skb(skb);
|
||||
return ERR_PTR(-EIO);
|
||||
}
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
static int btusb_setup_bcm92035(struct hci_dev *hdev)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
@ -1342,36 +1323,40 @@ static int btusb_setup_csr(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_rp_read_local_version *rp;
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
skb = btusb_read_local_version(hdev);
|
||||
if (IS_ERR(skb))
|
||||
return -PTR_ERR(skb);
|
||||
skb = __hci_cmd_sync(hdev, HCI_OP_READ_LOCAL_VERSION, 0, NULL,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
int err = PTR_ERR(skb);
|
||||
BT_ERR("%s: CSR: Local version failed (%d)", hdev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
if (skb->len != sizeof(struct hci_rp_read_local_version)) {
|
||||
BT_ERR("%s: CSR: Local version length mismatch", hdev->name);
|
||||
kfree_skb(skb);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
rp = (struct hci_rp_read_local_version *)skb->data;
|
||||
|
||||
if (!rp->status) {
|
||||
if (le16_to_cpu(rp->manufacturer) != 10) {
|
||||
/* Clear the reset quirk since this is not an actual
|
||||
* early Bluetooth 1.1 device from CSR.
|
||||
*/
|
||||
clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
if (le16_to_cpu(rp->manufacturer) != 10) {
|
||||
/* Clear the reset quirk since this is not an actual
|
||||
* early Bluetooth 1.1 device from CSR.
|
||||
*/
|
||||
clear_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
|
||||
/* These fake CSR controllers have all a broken
|
||||
* stored link key handling and so just disable it.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY,
|
||||
&hdev->quirks);
|
||||
}
|
||||
/* These fake CSR controllers have all a broken
|
||||
* stored link key handling and so just disable it.
|
||||
*/
|
||||
set_bit(HCI_QUIRK_BROKEN_STORED_LINK_KEY, &hdev->quirks);
|
||||
}
|
||||
|
||||
ret = -bt_to_errno(rp->status);
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct firmware *btusb_setup_intel_get_fw(struct hci_dev *hdev,
|
||||
@ -1614,6 +1599,8 @@ static int btusb_setup_intel(struct hci_dev *hdev)
|
||||
}
|
||||
fw_ptr = fw->data;
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
/* This Intel specific command enables the manufacturer mode of the
|
||||
* controller.
|
||||
*
|
||||
@ -1946,6 +1933,7 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
struct intel_boot_params *params;
|
||||
const struct firmware *fw;
|
||||
const u8 *fw_ptr;
|
||||
u32 frag_len;
|
||||
char fwname[64];
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
@ -2096,6 +2084,12 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
|
||||
BT_INFO("%s: Found device firmware: %s", hdev->name, fwname);
|
||||
|
||||
/* Save the DDC file name for later use to apply once the firmware
|
||||
* downloading is done.
|
||||
*/
|
||||
snprintf(fwname, sizeof(fwname), "intel/ibt-11-%u.ddc",
|
||||
le16_to_cpu(params->dev_revid));
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
if (fw->size < 644) {
|
||||
@ -2138,24 +2132,33 @@ static int btusb_setup_intel_new(struct hci_dev *hdev)
|
||||
}
|
||||
|
||||
fw_ptr = fw->data + 644;
|
||||
frag_len = 0;
|
||||
|
||||
while (fw_ptr - fw->data < fw->size) {
|
||||
struct hci_command_hdr *cmd = (void *)fw_ptr;
|
||||
u8 cmd_len;
|
||||
struct hci_command_hdr *cmd = (void *)(fw_ptr + frag_len);
|
||||
|
||||
cmd_len = sizeof(*cmd) + cmd->plen;
|
||||
frag_len += sizeof(*cmd) + cmd->plen;
|
||||
|
||||
/* Send each command from the firmware data buffer as
|
||||
* a single Data fragment.
|
||||
/* The paramter 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.
|
||||
*/
|
||||
err = btusb_intel_secure_send(hdev, 0x01, cmd_len, fw_ptr);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s: Failed to send firmware data (%d)",
|
||||
hdev->name, err);
|
||||
goto done;
|
||||
}
|
||||
if (!(frag_len % 4)) {
|
||||
err = btusb_intel_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 += cmd_len;
|
||||
fw_ptr += frag_len;
|
||||
frag_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
set_bit(BTUSB_FIRMWARE_LOADED, &data->flags);
|
||||
@ -2248,6 +2251,43 @@ done:
|
||||
|
||||
clear_bit(BTUSB_BOOTLOADER, &data->flags);
|
||||
|
||||
/* Once the device is running in operational mode, it needs to apply
|
||||
* the device configuration (DDC) parameters.
|
||||
*
|
||||
* The device can work without DDC parameters, so even if it fails
|
||||
* to load the file, no need to fail the setup.
|
||||
*/
|
||||
err = request_firmware_direct(&fw, fwname, &hdev->dev);
|
||||
if (err < 0)
|
||||
return 0;
|
||||
|
||||
BT_INFO("%s: Found Intel DDC parameters: %s", hdev->name, fwname);
|
||||
|
||||
fw_ptr = fw->data;
|
||||
|
||||
/* DDC file contains one or more DDC structure which has
|
||||
* Length (1 byte), DDC ID (2 bytes), and DDC value (Length - 2).
|
||||
*/
|
||||
while (fw->size > fw_ptr - fw->data) {
|
||||
u8 cmd_plen = fw_ptr[0] + sizeof(u8);
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc8b, cmd_plen, fw_ptr,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
BT_ERR("%s: Failed to send Intel_Write_DDC (%ld)",
|
||||
hdev->name, PTR_ERR(skb));
|
||||
release_firmware(fw);
|
||||
return PTR_ERR(skb);
|
||||
}
|
||||
|
||||
fw_ptr += cmd_plen;
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
release_firmware(fw);
|
||||
|
||||
BT_INFO("%s: Applying Intel DDC parameters completed", hdev->name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -192,6 +192,7 @@ static int ath_recv(struct hci_uart *hu, const void *data, int count)
|
||||
if (IS_ERR(ath->rx_skb)) {
|
||||
int err = PTR_ERR(ath->rx_skb);
|
||||
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
|
||||
ath->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -36,6 +37,55 @@ struct bcm_data {
|
||||
struct sk_buff_head txq;
|
||||
};
|
||||
|
||||
static int bcm_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||
{
|
||||
struct hci_dev *hdev = hu->hdev;
|
||||
struct sk_buff *skb;
|
||||
struct bcm_update_uart_baud_rate param;
|
||||
|
||||
if (speed > 3000000) {
|
||||
struct bcm_write_uart_clock_setting clock;
|
||||
|
||||
clock.type = BCM_UART_CLOCK_48MHZ;
|
||||
|
||||
BT_DBG("%s: Set Controller clock (%d)", hdev->name, clock.type);
|
||||
|
||||
/* This Broadcom specific command changes the UART's controller
|
||||
* clock for baud rate > 3000000.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc45, 1, &clock, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
int err = PTR_ERR(skb);
|
||||
BT_ERR("%s: BCM: failed to write clock command (%d)",
|
||||
hdev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
}
|
||||
|
||||
BT_DBG("%s: Set Controller UART speed to %d bit/s", hdev->name, speed);
|
||||
|
||||
param.zero = cpu_to_le16(0);
|
||||
param.baud_rate = cpu_to_le32(speed);
|
||||
|
||||
/* This Broadcom specific command changes the UART's controller baud
|
||||
* rate.
|
||||
*/
|
||||
skb = __hci_cmd_sync(hdev, 0xfc18, sizeof(param), ¶m,
|
||||
HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
int err = PTR_ERR(skb);
|
||||
BT_ERR("%s: BCM: failed to write update baudrate command (%d)",
|
||||
hdev->name, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bcm_open(struct hci_uart *hu)
|
||||
{
|
||||
struct bcm_data *bcm;
|
||||
@ -79,11 +129,62 @@ static int bcm_flush(struct hci_uart *hu)
|
||||
|
||||
static int bcm_setup(struct hci_uart *hu)
|
||||
{
|
||||
char fw_name[64];
|
||||
const struct firmware *fw;
|
||||
unsigned int speed;
|
||||
int err;
|
||||
|
||||
BT_DBG("hu %p", hu);
|
||||
|
||||
hu->hdev->set_bdaddr = btbcm_set_bdaddr;
|
||||
|
||||
return btbcm_setup_patchram(hu->hdev);
|
||||
err = btbcm_initialize(hu->hdev, fw_name, sizeof(fw_name));
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
err = request_firmware(&fw, fw_name, &hu->hdev->dev);
|
||||
if (err < 0) {
|
||||
BT_INFO("%s: BCM: Patch %s not found", hu->hdev->name, fw_name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
err = btbcm_patchram(hu->hdev, fw);
|
||||
if (err) {
|
||||
BT_INFO("%s: BCM: Patch failed (%d)", hu->hdev->name, err);
|
||||
goto finalize;
|
||||
}
|
||||
|
||||
/* Init speed if any */
|
||||
if (hu->init_speed)
|
||||
speed = hu->init_speed;
|
||||
else if (hu->proto->init_speed)
|
||||
speed = hu->proto->init_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (speed)
|
||||
hci_uart_set_baudrate(hu, speed);
|
||||
|
||||
/* Operational speed if any */
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (speed) {
|
||||
err = bcm_set_baudrate(hu, speed);
|
||||
if (!err)
|
||||
hci_uart_set_baudrate(hu, speed);
|
||||
}
|
||||
|
||||
finalize:
|
||||
release_firmware(fw);
|
||||
|
||||
err = btbcm_finalize(hu->hdev);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct h4_recv_pkt bcm_recv_pkts[] = {
|
||||
@ -104,6 +205,7 @@ static int bcm_recv(struct hci_uart *hu, const void *data, int count)
|
||||
if (IS_ERR(bcm->rx_skb)) {
|
||||
int err = PTR_ERR(bcm->rx_skb);
|
||||
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
|
||||
bcm->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
@ -133,10 +235,13 @@ static struct sk_buff *bcm_dequeue(struct hci_uart *hu)
|
||||
static const struct hci_uart_proto bcm_proto = {
|
||||
.id = HCI_UART_BCM,
|
||||
.name = "BCM",
|
||||
.init_speed = 115200,
|
||||
.oper_speed = 4000000,
|
||||
.open = bcm_open,
|
||||
.close = bcm_close,
|
||||
.flush = bcm_flush,
|
||||
.setup = bcm_setup,
|
||||
.set_baudrate = bcm_set_baudrate,
|
||||
.recv = bcm_recv,
|
||||
.enqueue = bcm_enqueue,
|
||||
.dequeue = bcm_dequeue,
|
||||
|
@ -436,7 +436,7 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
|
||||
break;
|
||||
default:
|
||||
memcpy(skb_put(bcsp->rx_skb, 1), &byte, 1);
|
||||
if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
|
||||
if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
|
||||
bcsp->rx_state != BCSP_W4_CRC)
|
||||
bcsp_crc_update(&bcsp->message_crc, byte);
|
||||
bcsp->rx_count--;
|
||||
@ -447,24 +447,24 @@ static inline void bcsp_unslip_one_byte(struct bcsp_struct *bcsp, unsigned char
|
||||
switch (byte) {
|
||||
case 0xdc:
|
||||
memcpy(skb_put(bcsp->rx_skb, 1), &c0, 1);
|
||||
if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
|
||||
if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
|
||||
bcsp->rx_state != BCSP_W4_CRC)
|
||||
bcsp_crc_update(&bcsp-> message_crc, 0xc0);
|
||||
bcsp_crc_update(&bcsp->message_crc, 0xc0);
|
||||
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
|
||||
bcsp->rx_count--;
|
||||
break;
|
||||
|
||||
case 0xdd:
|
||||
memcpy(skb_put(bcsp->rx_skb, 1), &db, 1);
|
||||
if ((bcsp->rx_skb-> data[0] & 0x40) != 0 &&
|
||||
if ((bcsp->rx_skb->data[0] & 0x40) != 0 &&
|
||||
bcsp->rx_state != BCSP_W4_CRC)
|
||||
bcsp_crc_update(&bcsp-> message_crc, 0xdb);
|
||||
bcsp_crc_update(&bcsp->message_crc, 0xdb);
|
||||
bcsp->rx_esc_state = BCSP_ESCSTATE_NOESC;
|
||||
bcsp->rx_count--;
|
||||
break;
|
||||
|
||||
default:
|
||||
BT_ERR ("Invalid byte %02x after esc byte", byte);
|
||||
BT_ERR("Invalid byte %02x after esc byte", byte);
|
||||
kfree_skb(bcsp->rx_skb);
|
||||
bcsp->rx_skb = NULL;
|
||||
bcsp->rx_state = BCSP_W4_PKT_DELIMITER;
|
||||
@ -527,7 +527,7 @@ static void bcsp_complete_rx_pkt(struct hci_uart *hu)
|
||||
|
||||
hci_recv_frame(hu->hdev, bcsp->rx_skb);
|
||||
} else {
|
||||
BT_ERR ("Packet for unknown channel (%u %s)",
|
||||
BT_ERR("Packet for unknown channel (%u %s)",
|
||||
bcsp->rx_skb->data[1] & 0x0f,
|
||||
bcsp->rx_skb->data[0] & 0x80 ?
|
||||
"reliable" : "unreliable");
|
||||
@ -587,7 +587,7 @@ static int bcsp_recv(struct hci_uart *hu, const void *data, int count)
|
||||
}
|
||||
if (bcsp->rx_skb->data[0] & 0x80 /* reliable pkt */
|
||||
&& (bcsp->rx_skb->data[0] & 0x07) != bcsp->rxseq_txack) {
|
||||
BT_ERR ("Out-of-order packet arrived, got %u expected %u",
|
||||
BT_ERR("Out-of-order packet arrived, got %u expected %u",
|
||||
bcsp->rx_skb->data[0] & 0x07, bcsp->rxseq_txack);
|
||||
|
||||
kfree_skb(bcsp->rx_skb);
|
||||
|
@ -133,6 +133,7 @@ static int h4_recv(struct hci_uart *hu, const void *data, int count)
|
||||
if (IS_ERR(h4->rx_skb)) {
|
||||
int err = PTR_ERR(h4->rx_skb);
|
||||
BT_ERR("%s: Frame reassembly failed (%d)", hu->hdev->name, err);
|
||||
h4->rx_skb = NULL;
|
||||
return err;
|
||||
}
|
||||
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <linux/signal.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
@ -265,11 +266,133 @@ static int hci_uart_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Flow control or un-flow control the device */
|
||||
void hci_uart_set_flow_control(struct hci_uart *hu, bool enable)
|
||||
{
|
||||
struct tty_struct *tty = hu->tty;
|
||||
struct ktermios ktermios;
|
||||
int status;
|
||||
unsigned int set = 0;
|
||||
unsigned int clear = 0;
|
||||
|
||||
if (enable) {
|
||||
/* Disable hardware flow control */
|
||||
ktermios = tty->termios;
|
||||
ktermios.c_cflag &= ~CRTSCTS;
|
||||
status = tty_set_termios(tty, &ktermios);
|
||||
BT_DBG("Disabling hardware flow control: %s",
|
||||
status ? "failed" : "success");
|
||||
|
||||
/* Clear RTS to prevent the device from sending */
|
||||
/* Most UARTs need OUT2 to enable interrupts */
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
BT_DBG("Current tiocm 0x%x", status);
|
||||
|
||||
set &= ~(TIOCM_OUT2 | TIOCM_RTS);
|
||||
clear = ~set;
|
||||
set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
|
||||
TIOCM_OUT2 | TIOCM_LOOP;
|
||||
clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
|
||||
TIOCM_OUT2 | TIOCM_LOOP;
|
||||
status = tty->driver->ops->tiocmset(tty, set, clear);
|
||||
BT_DBG("Clearing RTS: %s", status ? "failed" : "success");
|
||||
} else {
|
||||
/* Set RTS to allow the device to send again */
|
||||
status = tty->driver->ops->tiocmget(tty);
|
||||
BT_DBG("Current tiocm 0x%x", status);
|
||||
|
||||
set |= (TIOCM_OUT2 | TIOCM_RTS);
|
||||
clear = ~set;
|
||||
set &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
|
||||
TIOCM_OUT2 | TIOCM_LOOP;
|
||||
clear &= TIOCM_DTR | TIOCM_RTS | TIOCM_OUT1 |
|
||||
TIOCM_OUT2 | TIOCM_LOOP;
|
||||
status = tty->driver->ops->tiocmset(tty, set, clear);
|
||||
BT_DBG("Setting RTS: %s", status ? "failed" : "success");
|
||||
|
||||
/* Re-enable hardware flow control */
|
||||
ktermios = tty->termios;
|
||||
ktermios.c_cflag |= CRTSCTS;
|
||||
status = tty_set_termios(tty, &ktermios);
|
||||
BT_DBG("Enabling hardware flow control: %s",
|
||||
status ? "failed" : "success");
|
||||
}
|
||||
}
|
||||
|
||||
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
|
||||
unsigned int oper_speed)
|
||||
{
|
||||
hu->init_speed = init_speed;
|
||||
hu->oper_speed = oper_speed;
|
||||
}
|
||||
|
||||
void hci_uart_init_tty(struct hci_uart *hu)
|
||||
{
|
||||
struct tty_struct *tty = hu->tty;
|
||||
struct ktermios ktermios;
|
||||
|
||||
/* Bring the UART into a known 8 bits no parity hw fc state */
|
||||
ktermios = tty->termios;
|
||||
ktermios.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP |
|
||||
INLCR | IGNCR | ICRNL | IXON);
|
||||
ktermios.c_oflag &= ~OPOST;
|
||||
ktermios.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
|
||||
ktermios.c_cflag &= ~(CSIZE | PARENB);
|
||||
ktermios.c_cflag |= CS8;
|
||||
ktermios.c_cflag |= CRTSCTS;
|
||||
|
||||
/* tty_set_termios() return not checked as it is always 0 */
|
||||
tty_set_termios(tty, &ktermios);
|
||||
}
|
||||
|
||||
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
||||
{
|
||||
struct tty_struct *tty = hu->tty;
|
||||
struct ktermios ktermios;
|
||||
|
||||
ktermios = tty->termios;
|
||||
ktermios.c_cflag &= ~CBAUD;
|
||||
tty_termios_encode_baud_rate(&ktermios, speed, speed);
|
||||
|
||||
/* tty_set_termios() return not checked as it is always 0 */
|
||||
tty_set_termios(tty, &ktermios);
|
||||
|
||||
BT_DBG("%s: New tty speeds: %d/%d", hu->hdev->name,
|
||||
tty->termios.c_ispeed, tty->termios.c_ospeed);
|
||||
}
|
||||
|
||||
static int hci_uart_setup(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_uart *hu = hci_get_drvdata(hdev);
|
||||
struct hci_rp_read_local_version *ver;
|
||||
struct sk_buff *skb;
|
||||
unsigned int speed;
|
||||
int err;
|
||||
|
||||
/* Init speed if any */
|
||||
if (hu->init_speed)
|
||||
speed = hu->init_speed;
|
||||
else if (hu->proto->init_speed)
|
||||
speed = hu->proto->init_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (speed)
|
||||
hci_uart_set_baudrate(hu, speed);
|
||||
|
||||
/* Operational speed if any */
|
||||
if (hu->oper_speed)
|
||||
speed = hu->oper_speed;
|
||||
else if (hu->proto->oper_speed)
|
||||
speed = hu->proto->oper_speed;
|
||||
else
|
||||
speed = 0;
|
||||
|
||||
if (hu->proto->set_baudrate && speed) {
|
||||
err = hu->proto->set_baudrate(hu, speed);
|
||||
if (!err)
|
||||
hci_uart_set_baudrate(hu, speed);
|
||||
}
|
||||
|
||||
if (hu->proto->setup)
|
||||
return hu->proto->setup(hu);
|
||||
|
@ -58,10 +58,13 @@ struct hci_uart;
|
||||
struct hci_uart_proto {
|
||||
unsigned int id;
|
||||
const char *name;
|
||||
unsigned int init_speed;
|
||||
unsigned int oper_speed;
|
||||
int (*open)(struct hci_uart *hu);
|
||||
int (*close)(struct hci_uart *hu);
|
||||
int (*flush)(struct hci_uart *hu);
|
||||
int (*setup)(struct hci_uart *hu);
|
||||
int (*set_baudrate)(struct hci_uart *hu, unsigned int speed);
|
||||
int (*recv)(struct hci_uart *hu, const void *data, int len);
|
||||
int (*enqueue)(struct hci_uart *hu, struct sk_buff *skb);
|
||||
struct sk_buff *(*dequeue)(struct hci_uart *hu);
|
||||
@ -82,6 +85,9 @@ struct hci_uart {
|
||||
struct sk_buff *tx_skb;
|
||||
unsigned long tx_state;
|
||||
spinlock_t rx_lock;
|
||||
|
||||
unsigned int init_speed;
|
||||
unsigned int oper_speed;
|
||||
};
|
||||
|
||||
/* HCI_UART proto flag bits */
|
||||
@ -96,6 +102,11 @@ int hci_uart_register_proto(const struct hci_uart_proto *p);
|
||||
int hci_uart_unregister_proto(const struct hci_uart_proto *p);
|
||||
int hci_uart_tx_wakeup(struct hci_uart *hu);
|
||||
int hci_uart_init_ready(struct hci_uart *hu);
|
||||
void hci_uart_init_tty(struct hci_uart *hu);
|
||||
void hci_uart_set_baudrate(struct hci_uart *hu, unsigned int speed);
|
||||
void hci_uart_set_flow_control(struct hci_uart *hu, bool enable);
|
||||
void hci_uart_set_speeds(struct hci_uart *hu, unsigned int init_speed,
|
||||
unsigned int oper_speed);
|
||||
|
||||
#ifdef CONFIG_BT_HCIUART_H4
|
||||
int h4_init(void);
|
||||
|
@ -366,7 +366,7 @@ static const struct file_operations vhci_fops = {
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct miscdevice vhci_miscdev= {
|
||||
static struct miscdevice vhci_miscdev = {
|
||||
.name = "vhci",
|
||||
.fops = &vhci_fops,
|
||||
.minor = VHCI_MINOR,
|
||||
|
@ -47,6 +47,8 @@ struct at86rf2xx_chip_data {
|
||||
u16 t_reset_to_off;
|
||||
u16 t_off_to_aack;
|
||||
u16 t_off_to_tx_on;
|
||||
u16 t_off_to_sleep;
|
||||
u16 t_sleep_to_off;
|
||||
u16 t_frame;
|
||||
u16 t_p_ack;
|
||||
int rssi_base_val;
|
||||
@ -88,6 +90,7 @@ struct at86rf230_local {
|
||||
struct at86rf2xx_chip_data *data;
|
||||
struct regmap *regmap;
|
||||
int slp_tr;
|
||||
bool sleep;
|
||||
|
||||
struct completion state_complete;
|
||||
struct at86rf230_state_change state;
|
||||
@ -112,18 +115,66 @@ at86rf230_async_state_change(struct at86rf230_local *lp,
|
||||
const u8 state, void (*complete)(void *context),
|
||||
const bool irq_enable);
|
||||
|
||||
static inline void
|
||||
at86rf230_sleep(struct at86rf230_local *lp)
|
||||
{
|
||||
if (gpio_is_valid(lp->slp_tr)) {
|
||||
gpio_set_value(lp->slp_tr, 1);
|
||||
usleep_range(lp->data->t_off_to_sleep,
|
||||
lp->data->t_off_to_sleep + 10);
|
||||
lp->sleep = true;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
at86rf230_awake(struct at86rf230_local *lp)
|
||||
{
|
||||
if (gpio_is_valid(lp->slp_tr)) {
|
||||
gpio_set_value(lp->slp_tr, 0);
|
||||
usleep_range(lp->data->t_sleep_to_off,
|
||||
lp->data->t_sleep_to_off + 100);
|
||||
lp->sleep = false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
__at86rf230_write(struct at86rf230_local *lp,
|
||||
unsigned int addr, unsigned int data)
|
||||
{
|
||||
return regmap_write(lp->regmap, addr, data);
|
||||
bool sleep = lp->sleep;
|
||||
int ret;
|
||||
|
||||
/* awake for register setting if sleep */
|
||||
if (sleep)
|
||||
at86rf230_awake(lp);
|
||||
|
||||
ret = regmap_write(lp->regmap, addr, data);
|
||||
|
||||
/* sleep again if was sleeping */
|
||||
if (sleep)
|
||||
at86rf230_sleep(lp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
__at86rf230_read(struct at86rf230_local *lp,
|
||||
unsigned int addr, unsigned int *data)
|
||||
{
|
||||
return regmap_read(lp->regmap, addr, data);
|
||||
bool sleep = lp->sleep;
|
||||
int ret;
|
||||
|
||||
/* awake for register setting if sleep */
|
||||
if (sleep)
|
||||
at86rf230_awake(lp);
|
||||
|
||||
ret = regmap_read(lp->regmap, addr, data);
|
||||
|
||||
/* sleep again if was sleeping */
|
||||
if (sleep)
|
||||
at86rf230_sleep(lp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
@ -145,7 +196,20 @@ at86rf230_write_subreg(struct at86rf230_local *lp,
|
||||
unsigned int addr, unsigned int mask,
|
||||
unsigned int shift, unsigned int data)
|
||||
{
|
||||
return regmap_update_bits(lp->regmap, addr, mask, data << shift);
|
||||
bool sleep = lp->sleep;
|
||||
int ret;
|
||||
|
||||
/* awake for register setting if sleep */
|
||||
if (sleep)
|
||||
at86rf230_awake(lp);
|
||||
|
||||
ret = regmap_update_bits(lp->regmap, addr, mask, data << shift);
|
||||
|
||||
/* sleep again if was sleeping */
|
||||
if (sleep)
|
||||
at86rf230_sleep(lp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void
|
||||
@ -869,13 +933,34 @@ at86rf230_ed(struct ieee802154_hw *hw, u8 *level)
|
||||
static int
|
||||
at86rf230_start(struct ieee802154_hw *hw)
|
||||
{
|
||||
return at86rf230_sync_state_change(hw->priv, STATE_RX_AACK_ON);
|
||||
struct at86rf230_local *lp = hw->priv;
|
||||
|
||||
at86rf230_awake(lp);
|
||||
enable_irq(lp->spi->irq);
|
||||
|
||||
return at86rf230_sync_state_change(lp, STATE_RX_AACK_ON);
|
||||
}
|
||||
|
||||
static void
|
||||
at86rf230_stop(struct ieee802154_hw *hw)
|
||||
{
|
||||
at86rf230_sync_state_change(hw->priv, STATE_FORCE_TRX_OFF);
|
||||
struct at86rf230_local *lp = hw->priv;
|
||||
u8 csma_seed[2];
|
||||
|
||||
at86rf230_sync_state_change(lp, STATE_FORCE_TRX_OFF);
|
||||
|
||||
disable_irq(lp->spi->irq);
|
||||
|
||||
/* It's recommended to set random new csma_seeds before sleep state.
|
||||
* Makes only sense in the stop callback, not doing this inside of
|
||||
* at86rf230_sleep, this is also used when we don't transmit afterwards
|
||||
* when calling start callback again.
|
||||
*/
|
||||
get_random_bytes(csma_seed, ARRAY_SIZE(csma_seed));
|
||||
at86rf230_write_subreg(lp, SR_CSMA_SEED_0, csma_seed[0]);
|
||||
at86rf230_write_subreg(lp, SR_CSMA_SEED_1, csma_seed[1]);
|
||||
|
||||
at86rf230_sleep(lp);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1241,6 +1326,8 @@ static struct at86rf2xx_chip_data at86rf233_data = {
|
||||
.t_reset_to_off = 26,
|
||||
.t_off_to_aack = 80,
|
||||
.t_off_to_tx_on = 80,
|
||||
.t_off_to_sleep = 35,
|
||||
.t_sleep_to_off = 210,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.rssi_base_val = -91,
|
||||
@ -1254,6 +1341,8 @@ static struct at86rf2xx_chip_data at86rf231_data = {
|
||||
.t_reset_to_off = 37,
|
||||
.t_off_to_aack = 110,
|
||||
.t_off_to_tx_on = 110,
|
||||
.t_off_to_sleep = 35,
|
||||
.t_sleep_to_off = 380,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.rssi_base_val = -91,
|
||||
@ -1267,6 +1356,8 @@ static struct at86rf2xx_chip_data at86rf212_data = {
|
||||
.t_reset_to_off = 26,
|
||||
.t_off_to_aack = 200,
|
||||
.t_off_to_tx_on = 200,
|
||||
.t_off_to_sleep = 35,
|
||||
.t_sleep_to_off = 380,
|
||||
.t_frame = 4096,
|
||||
.t_p_ack = 545,
|
||||
.rssi_base_val = -100,
|
||||
@ -1443,7 +1534,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK |
|
||||
lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM |
|
||||
IEEE802154_HW_CSMA_PARAMS |
|
||||
IEEE802154_HW_FRAME_RETRIES | IEEE802154_HW_AFILT |
|
||||
IEEE802154_HW_PROMISCUOUS;
|
||||
@ -1602,7 +1693,6 @@ static int at86rf230_probe(struct spi_device *spi)
|
||||
lp->spi = spi;
|
||||
lp->slp_tr = slp_tr;
|
||||
hw->parent = &spi->dev;
|
||||
hw->vif_data_size = sizeof(*lp);
|
||||
ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
|
||||
|
||||
lp->regmap = devm_regmap_init_spi(spi, &at86rf230_regmap_spi_config);
|
||||
@ -1634,13 +1724,19 @@ static int at86rf230_probe(struct spi_device *spi)
|
||||
|
||||
irq_type = irq_get_trigger_type(spi->irq);
|
||||
if (!irq_type)
|
||||
irq_type = IRQF_TRIGGER_RISING;
|
||||
irq_type = IRQF_TRIGGER_HIGH;
|
||||
|
||||
rc = devm_request_irq(&spi->dev, spi->irq, at86rf230_isr,
|
||||
IRQF_SHARED | irq_type, dev_name(&spi->dev), lp);
|
||||
if (rc)
|
||||
goto free_dev;
|
||||
|
||||
/* disable_irq by default and wait for starting hardware */
|
||||
disable_irq(spi->irq);
|
||||
|
||||
/* going into sleep by default */
|
||||
at86rf230_sleep(lp);
|
||||
|
||||
rc = ieee802154_register_hw(lp->hw);
|
||||
if (rc)
|
||||
goto free_dev;
|
||||
|
@ -58,17 +58,6 @@ struct atusb {
|
||||
uint8_t tx_ack_seq; /* current TX ACK sequence number */
|
||||
};
|
||||
|
||||
/* at86rf230.h defines values as <reg, mask, shift> tuples. We use the more
|
||||
* traditional style of having registers and or-able values. SR_REG extracts
|
||||
* the register number. SR_VALUE uses the shift to prepare a value accordingly.
|
||||
*/
|
||||
|
||||
#define __SR_REG(reg, mask, shift) (reg)
|
||||
#define SR_REG(sr) __SR_REG(sr)
|
||||
|
||||
#define __SR_VALUE(reg, mask, shift, val) ((val) << (shift))
|
||||
#define SR_VALUE(sr, val) __SR_VALUE(sr, (val))
|
||||
|
||||
/* ----- USB commands without data ----------------------------------------- */
|
||||
|
||||
/* To reduce the number of error checks in the code, we record the first error
|
||||
@ -130,6 +119,30 @@ static int atusb_read_reg(struct atusb *atusb, uint8_t reg)
|
||||
return ret >= 0 ? value : ret;
|
||||
}
|
||||
|
||||
static int atusb_write_subreg(struct atusb *atusb, uint8_t reg, uint8_t mask,
|
||||
uint8_t shift, uint8_t value)
|
||||
{
|
||||
struct usb_device *usb_dev = atusb->usb_dev;
|
||||
uint8_t orig, tmp;
|
||||
int ret = 0;
|
||||
|
||||
dev_dbg(&usb_dev->dev, "atusb_write_subreg: 0x%02x <- 0x%02x\n",
|
||||
reg, value);
|
||||
|
||||
orig = atusb_read_reg(atusb, reg);
|
||||
|
||||
/* Write the value only into that part of the register which is allowed
|
||||
* by the mask. All other bits stay as before.
|
||||
*/
|
||||
tmp = orig & ~mask;
|
||||
tmp |= (value << shift) & mask;
|
||||
|
||||
if (tmp != orig)
|
||||
ret = atusb_write_reg(atusb, reg, tmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int atusb_get_and_clear_error(struct atusb *atusb)
|
||||
{
|
||||
int err = atusb->err;
|
||||
@ -376,7 +389,6 @@ static int atusb_set_hw_addr_filt(struct ieee802154_hw *hw,
|
||||
{
|
||||
struct atusb *atusb = hw->priv;
|
||||
struct device *dev = &atusb->usb_dev->dev;
|
||||
uint8_t reg;
|
||||
|
||||
if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
|
||||
u16 addr = le16_to_cpu(filt->short_addr);
|
||||
@ -406,12 +418,10 @@ static int atusb_set_hw_addr_filt(struct ieee802154_hw *hw,
|
||||
if (changed & IEEE802154_AFILT_PANC_CHANGED) {
|
||||
dev_vdbg(dev,
|
||||
"atusb_set_hw_addr_filt called for panc change\n");
|
||||
reg = atusb_read_reg(atusb, SR_REG(SR_AACK_I_AM_COORD));
|
||||
if (filt->pan_coord)
|
||||
reg |= SR_VALUE(SR_AACK_I_AM_COORD, 1);
|
||||
atusb_write_subreg(atusb, SR_AACK_I_AM_COORD, 1);
|
||||
else
|
||||
reg &= ~SR_VALUE(SR_AACK_I_AM_COORD, 1);
|
||||
atusb_write_reg(atusb, SR_REG(SR_AACK_I_AM_COORD), reg);
|
||||
atusb_write_subreg(atusb, SR_AACK_I_AM_COORD, 0);
|
||||
}
|
||||
|
||||
return atusb_get_and_clear_error(atusb);
|
||||
@ -443,6 +453,53 @@ static void atusb_stop(struct ieee802154_hw *hw)
|
||||
atusb_get_and_clear_error(atusb);
|
||||
}
|
||||
|
||||
#define ATUSB_MAX_TX_POWERS 0xF
|
||||
static const s32 atusb_powers[ATUSB_MAX_TX_POWERS + 1] = {
|
||||
300, 280, 230, 180, 130, 70, 0, -100, -200, -300, -400, -500, -700,
|
||||
-900, -1200, -1700,
|
||||
};
|
||||
|
||||
static int
|
||||
atusb_set_txpower(struct ieee802154_hw *hw, s32 mbm)
|
||||
{
|
||||
struct atusb *atusb = hw->priv;
|
||||
u32 i;
|
||||
|
||||
for (i = 0; i < hw->phy->supported.tx_powers_size; i++) {
|
||||
if (hw->phy->supported.tx_powers[i] == mbm)
|
||||
return atusb_write_subreg(atusb, SR_TX_PWR_23X, i);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int
|
||||
atusb_set_promiscuous_mode(struct ieee802154_hw *hw, const bool on)
|
||||
{
|
||||
struct atusb *atusb = hw->priv;
|
||||
int ret;
|
||||
|
||||
if (on) {
|
||||
ret = atusb_write_subreg(atusb, SR_AACK_DIS_ACK, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = atusb_write_subreg(atusb, SR_AACK_PROM_MODE, 1);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = atusb_write_subreg(atusb, SR_AACK_PROM_MODE, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = atusb_write_subreg(atusb, SR_AACK_DIS_ACK, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct ieee802154_ops atusb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.xmit_async = atusb_xmit,
|
||||
@ -451,6 +508,8 @@ static struct ieee802154_ops atusb_ops = {
|
||||
.start = atusb_start,
|
||||
.stop = atusb_stop,
|
||||
.set_hw_addr_filt = atusb_set_hw_addr_filt,
|
||||
.set_txpower = atusb_set_txpower,
|
||||
.set_promiscuous_mode = atusb_set_promiscuous_mode,
|
||||
};
|
||||
|
||||
/* ----- Firmware and chip version information ----------------------------- */
|
||||
@ -569,11 +628,16 @@ static int atusb_probe(struct usb_interface *interface,
|
||||
|
||||
hw->parent = &usb_dev->dev;
|
||||
hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AFILT |
|
||||
IEEE802154_HW_AACK;
|
||||
IEEE802154_HW_PROMISCUOUS;
|
||||
|
||||
hw->phy->flags = WPAN_PHY_FLAG_TXPOWER;
|
||||
|
||||
hw->phy->current_page = 0;
|
||||
hw->phy->current_channel = 11; /* reset default */
|
||||
hw->phy->supported.channels[0] = 0x7FFF800;
|
||||
hw->phy->supported.tx_powers = atusb_powers;
|
||||
hw->phy->supported.tx_powers_size = ARRAY_SIZE(atusb_powers);
|
||||
hw->phy->transmit_power = hw->phy->supported.tx_powers[0];
|
||||
ieee802154_random_extended_addr(&hw->phy->perm_extended_addr);
|
||||
|
||||
atusb_command(atusb, ATUSB_RF_RESET, 0);
|
||||
@ -622,8 +686,7 @@ static int atusb_probe(struct usb_interface *interface,
|
||||
* http://www.jennic.com/download_file.php?supportFile=JN-AN-1035%20Calculating%20802-15-4%20Data%20Rates-1v0.pdf
|
||||
*/
|
||||
|
||||
atusb_write_reg(atusb,
|
||||
SR_REG(SR_RX_SAFE_MODE), SR_VALUE(SR_RX_SAFE_MODE, 1));
|
||||
atusb_write_subreg(atusb, SR_RX_SAFE_MODE, 1);
|
||||
#endif
|
||||
atusb_write_reg(atusb, RG_IRQ_MASK, 0xff);
|
||||
|
||||
|
@ -196,6 +196,7 @@ struct cc2520_private {
|
||||
u8 *buf; /* SPI TX/Rx data buffer */
|
||||
struct mutex buffer_mutex; /* SPI buffer mutex */
|
||||
bool is_tx; /* Flag for sync b/w Tx and Rx */
|
||||
bool amplified; /* Flag for CC2591 */
|
||||
int fifo_pin; /* FIFO GPIO pin number */
|
||||
struct work_struct fifop_irqwork;/* Workqueue for FIFOP */
|
||||
spinlock_t lock; /* Lock for is_tx*/
|
||||
@ -589,22 +590,23 @@ cc2520_filter(struct ieee802154_hw *hw,
|
||||
struct ieee802154_hw_addr_filt *filt, unsigned long changed)
|
||||
{
|
||||
struct cc2520_private *priv = hw->priv;
|
||||
int ret = 0;
|
||||
|
||||
if (changed & IEEE802154_AFILT_PANID_CHANGED) {
|
||||
u16 panid = le16_to_cpu(filt->pan_id);
|
||||
|
||||
dev_vdbg(&priv->spi->dev,
|
||||
"cc2520_filter called for pan id\n");
|
||||
cc2520_write_ram(priv, CC2520RAM_PANID,
|
||||
sizeof(panid), (u8 *)&panid);
|
||||
ret = cc2520_write_ram(priv, CC2520RAM_PANID,
|
||||
sizeof(panid), (u8 *)&panid);
|
||||
}
|
||||
|
||||
if (changed & IEEE802154_AFILT_IEEEADDR_CHANGED) {
|
||||
dev_vdbg(&priv->spi->dev,
|
||||
"cc2520_filter called for IEEE addr\n");
|
||||
cc2520_write_ram(priv, CC2520RAM_IEEEADDR,
|
||||
sizeof(filt->ieee_addr),
|
||||
(u8 *)&filt->ieee_addr);
|
||||
ret = cc2520_write_ram(priv, CC2520RAM_IEEEADDR,
|
||||
sizeof(filt->ieee_addr),
|
||||
(u8 *)&filt->ieee_addr);
|
||||
}
|
||||
|
||||
if (changed & IEEE802154_AFILT_SADDR_CHANGED) {
|
||||
@ -612,20 +614,113 @@ cc2520_filter(struct ieee802154_hw *hw,
|
||||
|
||||
dev_vdbg(&priv->spi->dev,
|
||||
"cc2520_filter called for saddr\n");
|
||||
cc2520_write_ram(priv, CC2520RAM_SHORTADDR,
|
||||
sizeof(addr), (u8 *)&addr);
|
||||
ret = cc2520_write_ram(priv, CC2520RAM_SHORTADDR,
|
||||
sizeof(addr), (u8 *)&addr);
|
||||
}
|
||||
|
||||
if (changed & IEEE802154_AFILT_PANC_CHANGED) {
|
||||
dev_vdbg(&priv->spi->dev,
|
||||
"cc2520_filter called for panc change\n");
|
||||
if (filt->pan_coord)
|
||||
cc2520_write_register(priv, CC2520_FRMFILT0, 0x02);
|
||||
ret = cc2520_write_register(priv, CC2520_FRMFILT0,
|
||||
0x02);
|
||||
else
|
||||
cc2520_write_register(priv, CC2520_FRMFILT0, 0x00);
|
||||
ret = cc2520_write_register(priv, CC2520_FRMFILT0,
|
||||
0x00);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int cc2520_set_tx_power(struct cc2520_private *priv, s32 mbm)
|
||||
{
|
||||
u8 power;
|
||||
|
||||
switch (mbm) {
|
||||
case 500:
|
||||
power = 0xF7;
|
||||
break;
|
||||
case 300:
|
||||
power = 0xF2;
|
||||
break;
|
||||
case 200:
|
||||
power = 0xAB;
|
||||
break;
|
||||
case 100:
|
||||
power = 0x13;
|
||||
break;
|
||||
case 0:
|
||||
power = 0x32;
|
||||
break;
|
||||
case -200:
|
||||
power = 0x81;
|
||||
break;
|
||||
case -400:
|
||||
power = 0x88;
|
||||
break;
|
||||
case -700:
|
||||
power = 0x2C;
|
||||
break;
|
||||
case -1800:
|
||||
power = 0x03;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cc2520_write_register(priv, CC2520_TXPOWER, power);
|
||||
}
|
||||
|
||||
static inline int cc2520_cc2591_set_tx_power(struct cc2520_private *priv,
|
||||
s32 mbm)
|
||||
{
|
||||
u8 power;
|
||||
|
||||
switch (mbm) {
|
||||
case 1700:
|
||||
power = 0xF9;
|
||||
break;
|
||||
case 1600:
|
||||
power = 0xF0;
|
||||
break;
|
||||
case 1400:
|
||||
power = 0xA0;
|
||||
break;
|
||||
case 1100:
|
||||
power = 0x2C;
|
||||
break;
|
||||
case -100:
|
||||
power = 0x03;
|
||||
break;
|
||||
case -800:
|
||||
power = 0x01;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return cc2520_write_register(priv, CC2520_TXPOWER, power);
|
||||
}
|
||||
|
||||
#define CC2520_MAX_TX_POWERS 0x8
|
||||
static const s32 cc2520_powers[CC2520_MAX_TX_POWERS + 1] = {
|
||||
500, 300, 200, 100, 0, -200, -400, -700, -1800,
|
||||
};
|
||||
|
||||
#define CC2520_CC2591_MAX_TX_POWERS 0x5
|
||||
static const s32 cc2520_cc2591_powers[CC2520_CC2591_MAX_TX_POWERS + 1] = {
|
||||
1700, 1600, 1400, 1100, -100, -800,
|
||||
};
|
||||
|
||||
static int
|
||||
cc2520_set_txpower(struct ieee802154_hw *hw, s32 mbm)
|
||||
{
|
||||
struct cc2520_private *priv = hw->priv;
|
||||
|
||||
if (!priv->amplified)
|
||||
return cc2520_set_tx_power(priv, mbm);
|
||||
|
||||
return cc2520_cc2591_set_tx_power(priv, mbm);
|
||||
}
|
||||
|
||||
static const struct ieee802154_ops cc2520_ops = {
|
||||
@ -636,6 +731,7 @@ static const struct ieee802154_ops cc2520_ops = {
|
||||
.ed = cc2520_ed,
|
||||
.set_channel = cc2520_set_channel,
|
||||
.set_hw_addr_filt = cc2520_filter,
|
||||
.set_txpower = cc2520_set_txpower,
|
||||
};
|
||||
|
||||
static int cc2520_register(struct cc2520_private *priv)
|
||||
@ -649,13 +745,25 @@ static int cc2520_register(struct cc2520_private *priv)
|
||||
priv->hw->priv = priv;
|
||||
priv->hw->parent = &priv->spi->dev;
|
||||
priv->hw->extra_tx_headroom = 0;
|
||||
priv->hw->vif_data_size = sizeof(*priv);
|
||||
ieee802154_random_extended_addr(&priv->hw->phy->perm_extended_addr);
|
||||
|
||||
/* We do support only 2.4 Ghz */
|
||||
priv->hw->phy->supported.channels[0] = 0x7FFF800;
|
||||
priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
|
||||
IEEE802154_HW_AFILT;
|
||||
priv->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
|
||||
|
||||
priv->hw->phy->flags = WPAN_PHY_FLAG_TXPOWER;
|
||||
|
||||
if (!priv->amplified) {
|
||||
priv->hw->phy->supported.tx_powers = cc2520_powers;
|
||||
priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_powers);
|
||||
priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[4];
|
||||
} else {
|
||||
priv->hw->phy->supported.tx_powers = cc2520_cc2591_powers;
|
||||
priv->hw->phy->supported.tx_powers_size = ARRAY_SIZE(cc2520_cc2591_powers);
|
||||
priv->hw->phy->transmit_power = priv->hw->phy->supported.tx_powers[0];
|
||||
}
|
||||
|
||||
priv->hw->phy->current_channel = 11;
|
||||
|
||||
dev_vdbg(&priv->spi->dev, "registered cc2520\n");
|
||||
ret = ieee802154_register_hw(priv->hw);
|
||||
@ -738,7 +846,9 @@ static int cc2520_get_platform_data(struct spi_device *spi,
|
||||
pdata->vreg = of_get_named_gpio(np, "vreg-gpio", 0);
|
||||
pdata->reset = of_get_named_gpio(np, "reset-gpio", 0);
|
||||
|
||||
pdata->amplified = of_property_read_bool(np, "amplified");
|
||||
/* CC2591 front end for CC2520 */
|
||||
if (of_property_read_bool(np, "amplified"))
|
||||
priv->amplified = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -781,11 +891,7 @@ static int cc2520_hw_init(struct cc2520_private *priv)
|
||||
* amplifier. See section 8 page 17 of TI application note AN065.
|
||||
* http://www.ti.com/lit/an/swra229a/swra229a.pdf
|
||||
*/
|
||||
if (pdata.amplified) {
|
||||
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF9);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
if (priv->amplified) {
|
||||
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x16);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
@ -806,10 +912,6 @@ static int cc2520_hw_init(struct cc2520_private *priv)
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
} else {
|
||||
ret = cc2520_write_register(priv, CC2520_TXPOWER, 0xF7);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
|
||||
ret = cc2520_write_register(priv, CC2520_AGCCTRL1, 0x11);
|
||||
if (ret)
|
||||
goto err_ret;
|
||||
@ -904,6 +1006,9 @@ static int cc2520_probe(struct spi_device *spi)
|
||||
spin_lock_init(&priv->lock);
|
||||
init_completion(&priv->tx_complete);
|
||||
|
||||
/* Assumption that CC2591 is not connected */
|
||||
priv->amplified = false;
|
||||
|
||||
/* Request all the gpio's */
|
||||
if (!gpio_is_valid(pdata.fifo)) {
|
||||
dev_err(&spi->dev, "fifo gpio is not valid\n");
|
||||
|
@ -41,6 +41,8 @@ struct fakelb_phy {
|
||||
u8 page;
|
||||
u8 channel;
|
||||
|
||||
bool suspended;
|
||||
|
||||
struct list_head list;
|
||||
struct list_head list_ifup;
|
||||
};
|
||||
@ -69,6 +71,7 @@ static int fakelb_hw_xmit(struct ieee802154_hw *hw, struct sk_buff *skb)
|
||||
struct fakelb_phy *current_phy = hw->priv, *phy;
|
||||
|
||||
read_lock_bh(&fakelb_ifup_phys_lock);
|
||||
WARN_ON(current_phy->suspended);
|
||||
list_for_each_entry(phy, &fakelb_ifup_phys, list_ifup) {
|
||||
if (current_phy == phy)
|
||||
continue;
|
||||
@ -92,6 +95,7 @@ static int fakelb_hw_start(struct ieee802154_hw *hw)
|
||||
struct fakelb_phy *phy = hw->priv;
|
||||
|
||||
write_lock_bh(&fakelb_ifup_phys_lock);
|
||||
phy->suspended = false;
|
||||
list_add(&phy->list_ifup, &fakelb_ifup_phys);
|
||||
write_unlock_bh(&fakelb_ifup_phys_lock);
|
||||
|
||||
@ -103,6 +107,7 @@ static void fakelb_hw_stop(struct ieee802154_hw *hw)
|
||||
struct fakelb_phy *phy = hw->priv;
|
||||
|
||||
write_lock_bh(&fakelb_ifup_phys_lock);
|
||||
phy->suspended = true;
|
||||
list_del(&phy->list_ifup);
|
||||
write_unlock_bh(&fakelb_ifup_phys_lock);
|
||||
}
|
||||
|
@ -533,6 +533,7 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
|
||||
u8 lqi = 0;
|
||||
u8 val;
|
||||
int ret = 0;
|
||||
int ret2;
|
||||
struct sk_buff *skb;
|
||||
|
||||
/* Turn off reception of packets off the air. This prevents the
|
||||
@ -569,9 +570,9 @@ static int mrf24j40_handle_rx(struct mrf24j40 *devrec)
|
||||
|
||||
out:
|
||||
/* Turn back on reception of packets off the air. */
|
||||
ret = read_short_reg(devrec, REG_BBREG1, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret2 = read_short_reg(devrec, REG_BBREG1, &val);
|
||||
if (ret2)
|
||||
return ret2;
|
||||
val &= ~0x4; /* Clear RXDECINV */
|
||||
write_short_reg(devrec, REG_BBREG1, val);
|
||||
|
||||
@ -751,8 +752,7 @@ static int mrf24j40_probe(struct spi_device *spi)
|
||||
devrec->hw->priv = devrec;
|
||||
devrec->hw->parent = &devrec->spi->dev;
|
||||
devrec->hw->phy->supported.channels[0] = CHANNEL_MASK;
|
||||
devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AACK |
|
||||
IEEE802154_HW_AFILT;
|
||||
devrec->hw->flags = IEEE802154_HW_OMIT_CKSUM | IEEE802154_HW_AFILT;
|
||||
|
||||
dev_dbg(printdev(devrec), "registered mrf24j40\n");
|
||||
ret = ieee802154_register_hw(devrec->hw);
|
||||
|
@ -225,15 +225,13 @@ static inline bool ieee802154_is_valid_psdu_len(const u8 len)
|
||||
* ieee802154_is_valid_psdu_len - check if extended addr is valid
|
||||
* @addr: extended addr to check
|
||||
*/
|
||||
static inline bool ieee802154_is_valid_extended_addr(const __le64 addr)
|
||||
static inline bool ieee802154_is_valid_extended_unicast_addr(const __le64 addr)
|
||||
{
|
||||
/* These EUI-64 addresses are reserved by IEEE. 0xffffffffffffffff
|
||||
* is used internally as extended to short address broadcast mapping.
|
||||
* This is currently a workaround because neighbor discovery can't
|
||||
* deal with short addresses types right now.
|
||||
/* Bail out if the address is all zero, or if the group
|
||||
* address bit is set.
|
||||
*/
|
||||
return ((addr != cpu_to_le64(0x0000000000000000ULL)) &&
|
||||
(addr != cpu_to_le64(0xffffffffffffffffULL)));
|
||||
!(addr & cpu_to_le64(0x0100000000000000ULL)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -244,9 +242,9 @@ static inline void ieee802154_random_extended_addr(__le64 *addr)
|
||||
{
|
||||
get_random_bytes(addr, IEEE802154_EXTENDED_ADDR_LEN);
|
||||
|
||||
/* toggle some bit if we hit an invalid extended addr */
|
||||
if (!ieee802154_is_valid_extended_addr(*addr))
|
||||
((u8 *)addr)[IEEE802154_EXTENDED_ADDR_LEN - 1] ^= 0x01;
|
||||
/* clear the group bit, and set the locally administered bit */
|
||||
((u8 *)addr)[IEEE802154_EXTENDED_ADDR_LEN - 1] &= ~0x01;
|
||||
((u8 *)addr)[IEEE802154_EXTENDED_ADDR_LEN - 1] |= 0x02;
|
||||
}
|
||||
|
||||
#endif /* LINUX_IEEE802154_H */
|
||||
|
@ -21,7 +21,6 @@ struct cc2520_platform_data {
|
||||
int sfd;
|
||||
int reset;
|
||||
int vreg;
|
||||
bool amplified;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -365,8 +365,19 @@ extern struct dentry *bt_debugfs;
|
||||
int l2cap_init(void);
|
||||
void l2cap_exit(void);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_BREDR)
|
||||
int sco_init(void);
|
||||
void sco_exit(void);
|
||||
#else
|
||||
static inline int sco_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sco_exit(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
int mgmt_init(void);
|
||||
void mgmt_exit(void);
|
||||
|
@ -1202,6 +1202,16 @@ struct hci_rp_read_clock {
|
||||
__le16 accuracy;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_ENC_KEY_SIZE 0x1408
|
||||
struct hci_cp_read_enc_key_size {
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
struct hci_rp_read_enc_key_size {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
__u8 key_size;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_READ_LOCAL_AMP_INFO 0x1409
|
||||
struct hci_rp_read_local_amp_info {
|
||||
__u8 status;
|
||||
|
@ -156,16 +156,22 @@ struct oob_data {
|
||||
};
|
||||
|
||||
struct adv_info {
|
||||
struct delayed_work timeout_exp;
|
||||
struct list_head list;
|
||||
bool pending;
|
||||
__u8 instance;
|
||||
__u32 flags;
|
||||
__u16 timeout;
|
||||
__u16 remaining_time;
|
||||
__u16 duration;
|
||||
__u16 adv_data_len;
|
||||
__u8 adv_data[HCI_MAX_AD_LENGTH];
|
||||
__u16 scan_rsp_len;
|
||||
__u8 scan_rsp_data[HCI_MAX_AD_LENGTH];
|
||||
};
|
||||
|
||||
#define HCI_MAX_ADV_INSTANCES 5
|
||||
#define HCI_DEFAULT_ADV_DURATION 2
|
||||
|
||||
#define HCI_MAX_SHORT_NAME_LENGTH 10
|
||||
|
||||
/* Default LE RPA expiry time, 15 minutes */
|
||||
@ -373,7 +379,11 @@ struct hci_dev {
|
||||
__u8 scan_rsp_data[HCI_MAX_AD_LENGTH];
|
||||
__u8 scan_rsp_data_len;
|
||||
|
||||
struct adv_info adv_instance;
|
||||
struct list_head adv_instances;
|
||||
unsigned int adv_instance_cnt;
|
||||
__u8 cur_adv_instance;
|
||||
__u16 adv_instance_timeout;
|
||||
struct delayed_work adv_instance_expire;
|
||||
|
||||
__u8 irk[16];
|
||||
__u32 rpa_timeout;
|
||||
@ -530,10 +540,22 @@ extern struct mutex hci_cb_list_lock;
|
||||
/* ----- HCI interface to upper protocols ----- */
|
||||
int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr);
|
||||
int l2cap_disconn_ind(struct hci_conn *hcon);
|
||||
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
|
||||
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_BREDR)
|
||||
int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 *flags);
|
||||
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
||||
void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb);
|
||||
#else
|
||||
static inline int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
__u8 *flags)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ----- Inquiry cache ----- */
|
||||
#define INQUIRY_CACHE_AGE_MAX (HZ*30) /* 30 seconds */
|
||||
@ -561,11 +583,6 @@ static inline void hci_discovery_filter_clear(struct hci_dev *hdev)
|
||||
hdev->discovery.scan_duration = 0;
|
||||
}
|
||||
|
||||
static inline void adv_info_init(struct hci_dev *hdev)
|
||||
{
|
||||
memset(&hdev->adv_instance, 0, sizeof(struct adv_info));
|
||||
}
|
||||
|
||||
bool hci_discovery_active(struct hci_dev *hdev);
|
||||
|
||||
void hci_discovery_set_state(struct hci_dev *hdev, int state);
|
||||
@ -1007,6 +1024,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
int hci_remove_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 bdaddr_type);
|
||||
|
||||
void hci_adv_instances_clear(struct hci_dev *hdev);
|
||||
struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance);
|
||||
struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance);
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration);
|
||||
int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance);
|
||||
|
||||
void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
|
||||
int hci_recv_frame(struct hci_dev *hdev, struct sk_buff *skb);
|
||||
@ -1350,6 +1376,7 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err);
|
||||
int mgmt_powered(struct hci_dev *hdev, u8 powered);
|
||||
int mgmt_update_adv_data(struct hci_dev *hdev);
|
||||
void mgmt_discoverable_timeout(struct hci_dev *hdev);
|
||||
void mgmt_adv_timeout_expired(struct hci_dev *hdev);
|
||||
void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key,
|
||||
bool persistent);
|
||||
void mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn,
|
||||
@ -1408,7 +1435,7 @@ void mgmt_smp_complete(struct hci_conn *conn, bool complete);
|
||||
u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
u16 to_multiplier);
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
__u8 ltk[16]);
|
||||
__u8 ltk[16], __u8 key_size);
|
||||
|
||||
void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 *bdaddr_type);
|
||||
|
@ -346,15 +346,15 @@ struct ieee802154_mac_params {
|
||||
struct wpan_phy;
|
||||
|
||||
enum {
|
||||
IEEE802154_LLSEC_PARAM_ENABLED = 1 << 0,
|
||||
IEEE802154_LLSEC_PARAM_FRAME_COUNTER = 1 << 1,
|
||||
IEEE802154_LLSEC_PARAM_OUT_LEVEL = 1 << 2,
|
||||
IEEE802154_LLSEC_PARAM_OUT_KEY = 1 << 3,
|
||||
IEEE802154_LLSEC_PARAM_KEY_SOURCE = 1 << 4,
|
||||
IEEE802154_LLSEC_PARAM_PAN_ID = 1 << 5,
|
||||
IEEE802154_LLSEC_PARAM_HWADDR = 1 << 6,
|
||||
IEEE802154_LLSEC_PARAM_COORD_HWADDR = 1 << 7,
|
||||
IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = 1 << 8,
|
||||
IEEE802154_LLSEC_PARAM_ENABLED = BIT(0),
|
||||
IEEE802154_LLSEC_PARAM_FRAME_COUNTER = BIT(1),
|
||||
IEEE802154_LLSEC_PARAM_OUT_LEVEL = BIT(2),
|
||||
IEEE802154_LLSEC_PARAM_OUT_KEY = BIT(3),
|
||||
IEEE802154_LLSEC_PARAM_KEY_SOURCE = BIT(4),
|
||||
IEEE802154_LLSEC_PARAM_PAN_ID = BIT(5),
|
||||
IEEE802154_LLSEC_PARAM_HWADDR = BIT(6),
|
||||
IEEE802154_LLSEC_PARAM_COORD_HWADDR = BIT(7),
|
||||
IEEE802154_LLSEC_PARAM_COORD_SHORTADDR = BIT(8),
|
||||
};
|
||||
|
||||
struct ieee802154_llsec_ops {
|
||||
|
@ -31,79 +31,117 @@
|
||||
*/
|
||||
#define MAC802154_FRAME_HARD_HEADER_LEN (2 + 1 + 20 + 14)
|
||||
|
||||
/* The following flags are used to indicate changed address settings from
|
||||
/**
|
||||
* enum ieee802154_hw_addr_filt_flags - hardware address filtering flags
|
||||
*
|
||||
* The following flags are used to indicate changed address settings from
|
||||
* the stack to the hardware.
|
||||
*
|
||||
* @IEEE802154_AFILT_SADDR_CHANGED: Indicates that the short address will be
|
||||
* change.
|
||||
*
|
||||
* @IEEE802154_AFILT_IEEEADDR_CHANGED: Indicates that the extended address
|
||||
* will be change.
|
||||
*
|
||||
* @IEEE802154_AFILT_PANID_CHANGED: Indicates that the pan id will be change.
|
||||
*
|
||||
* @IEEE802154_AFILT_PANC_CHANGED: Indicates that the address filter will
|
||||
* do frame address filtering as a pan coordinator.
|
||||
*/
|
||||
enum ieee802154_hw_addr_filt_flags {
|
||||
IEEE802154_AFILT_SADDR_CHANGED = BIT(0),
|
||||
IEEE802154_AFILT_IEEEADDR_CHANGED = BIT(1),
|
||||
IEEE802154_AFILT_PANID_CHANGED = BIT(2),
|
||||
IEEE802154_AFILT_PANC_CHANGED = BIT(3),
|
||||
};
|
||||
|
||||
/* indicates that the Short Address changed */
|
||||
#define IEEE802154_AFILT_SADDR_CHANGED 0x00000001
|
||||
/* indicates that the IEEE Address changed */
|
||||
#define IEEE802154_AFILT_IEEEADDR_CHANGED 0x00000002
|
||||
/* indicates that the PAN ID changed */
|
||||
#define IEEE802154_AFILT_PANID_CHANGED 0x00000004
|
||||
/* indicates that PAN Coordinator status changed */
|
||||
#define IEEE802154_AFILT_PANC_CHANGED 0x00000008
|
||||
|
||||
/**
|
||||
* struct ieee802154_hw_addr_filt - hardware address filtering settings
|
||||
*
|
||||
* @pan_id: pan_id which should be set to the hardware address filter.
|
||||
*
|
||||
* @short_addr: short_addr which should be set to the hardware address filter.
|
||||
*
|
||||
* @ieee_addr: extended address which should be set to the hardware address
|
||||
* filter.
|
||||
*
|
||||
* @pan_coord: boolean if hardware filtering should be operate as coordinator.
|
||||
*/
|
||||
struct ieee802154_hw_addr_filt {
|
||||
__le16 pan_id; /* Each independent PAN selects a unique
|
||||
* identifier. This PAN id allows communication
|
||||
* between devices within a network using short
|
||||
* addresses and enables transmissions between
|
||||
* devices across independent networks.
|
||||
*/
|
||||
__le16 pan_id;
|
||||
__le16 short_addr;
|
||||
__le64 ieee_addr;
|
||||
u8 pan_coord;
|
||||
};
|
||||
|
||||
struct ieee802154_vif {
|
||||
int type;
|
||||
|
||||
/* must be last */
|
||||
u8 drv_priv[0] __aligned(sizeof(void *));
|
||||
bool pan_coord;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct ieee802154_hw - ieee802154 hardware
|
||||
*
|
||||
* @extra_tx_headroom: headroom to reserve in each transmit skb for use by the
|
||||
* driver (e.g. for transmit headers.)
|
||||
*
|
||||
* @flags: hardware flags, see &enum ieee802154_hw_flags
|
||||
*
|
||||
* @parent: parent device of the hardware.
|
||||
*
|
||||
* @priv: pointer to private area that was allocated for driver use along with
|
||||
* this structure.
|
||||
*
|
||||
* @phy: This points to the &struct wpan_phy allocated for this 802.15.4 PHY.
|
||||
*/
|
||||
struct ieee802154_hw {
|
||||
/* filled by the driver */
|
||||
int extra_tx_headroom;
|
||||
u32 flags;
|
||||
struct device *parent;
|
||||
void *priv;
|
||||
|
||||
/* filled by mac802154 core */
|
||||
struct ieee802154_hw_addr_filt hw_filt;
|
||||
void *priv;
|
||||
struct wpan_phy *phy;
|
||||
size_t vif_data_size;
|
||||
};
|
||||
|
||||
/* Checksum is in hardware and is omitted from a packet
|
||||
/**
|
||||
* enum ieee802154_hw_flags - hardware flags
|
||||
*
|
||||
* These following flags are used to indicate hardware capabilities to
|
||||
* These flags are used to indicate hardware capabilities to
|
||||
* the stack. Generally, flags here should have their meaning
|
||||
* done in a way that the simplest hardware doesn't need setting
|
||||
* any particular flags. There are some exceptions to this rule,
|
||||
* however, so you are advised to review these flags carefully.
|
||||
*
|
||||
* @IEEE802154_HW_TX_OMIT_CKSUM: Indicates that xmitter will add FCS on it's
|
||||
* own.
|
||||
*
|
||||
* @IEEE802154_HW_LBT: Indicates that transceiver will support listen before
|
||||
* transmit.
|
||||
*
|
||||
* @IEEE802154_HW_CSMA_PARAMS: Indicates that transceiver will support csma
|
||||
* parameters (max_be, min_be, backoff exponents).
|
||||
*
|
||||
* @IEEE802154_HW_FRAME_RETRIES: Indicates that transceiver will support ARET
|
||||
* frame retries setting.
|
||||
*
|
||||
* @IEEE802154_HW_AFILT: Indicates that transceiver will support hardware
|
||||
* address filter setting.
|
||||
*
|
||||
* @IEEE802154_HW_PROMISCUOUS: Indicates that transceiver will support
|
||||
* promiscuous mode setting.
|
||||
*
|
||||
* @IEEE802154_HW_RX_OMIT_CKSUM: Indicates that receiver omits FCS.
|
||||
*
|
||||
* @IEEE802154_HW_RX_DROP_BAD_CKSUM: Indicates that receiver will not filter
|
||||
* frames with bad checksum.
|
||||
*/
|
||||
|
||||
/* Indicates that xmitter will add FCS on it's own. */
|
||||
#define IEEE802154_HW_TX_OMIT_CKSUM 0x00000001
|
||||
/* Indicates that receiver will autorespond with ACK frames. */
|
||||
#define IEEE802154_HW_AACK 0x00000002
|
||||
/* Indicates that transceiver will support listen before transmit. */
|
||||
#define IEEE802154_HW_LBT 0x00000004
|
||||
/* Indicates that transceiver will support csma (max_be, min_be, csma retries)
|
||||
* settings. */
|
||||
#define IEEE802154_HW_CSMA_PARAMS 0x00000008
|
||||
/* Indicates that transceiver will support ARET frame retries setting. */
|
||||
#define IEEE802154_HW_FRAME_RETRIES 0x00000010
|
||||
/* Indicates that transceiver will support hardware address filter setting. */
|
||||
#define IEEE802154_HW_AFILT 0x00000020
|
||||
/* Indicates that transceiver will support promiscuous mode setting. */
|
||||
#define IEEE802154_HW_PROMISCUOUS 0x00000040
|
||||
/* Indicates that receiver omits FCS. */
|
||||
#define IEEE802154_HW_RX_OMIT_CKSUM 0x00000080
|
||||
/* Indicates that receiver will not filter frames with bad checksum. */
|
||||
#define IEEE802154_HW_RX_DROP_BAD_CKSUM 0x00000100
|
||||
enum ieee802154_hw_flags {
|
||||
IEEE802154_HW_TX_OMIT_CKSUM = BIT(0),
|
||||
IEEE802154_HW_LBT = BIT(1),
|
||||
IEEE802154_HW_CSMA_PARAMS = BIT(2),
|
||||
IEEE802154_HW_FRAME_RETRIES = BIT(3),
|
||||
IEEE802154_HW_AFILT = BIT(4),
|
||||
IEEE802154_HW_PROMISCUOUS = BIT(5),
|
||||
IEEE802154_HW_RX_OMIT_CKSUM = BIT(6),
|
||||
IEEE802154_HW_RX_DROP_BAD_CKSUM = BIT(7),
|
||||
};
|
||||
|
||||
/* Indicates that receiver omits FCS and xmitter will add FCS on it's own. */
|
||||
#define IEEE802154_HW_OMIT_CKSUM (IEEE802154_HW_TX_OMIT_CKSUM | \
|
||||
|
@ -102,6 +102,8 @@ enum nl802154_attrs {
|
||||
|
||||
NL802154_ATTR_WPAN_PHY_CAPS,
|
||||
|
||||
NL802154_ATTR_SUPPORTED_COMMANDS,
|
||||
|
||||
/* add attributes here, update the policy in nl802154.c */
|
||||
|
||||
__NL802154_ATTR_AFTER_LAST,
|
||||
@ -185,7 +187,7 @@ enum nl802154_wpan_phy_capability_attr {
|
||||
* @NL802154_CCA_ENERGY_CARRIER: Carrier sense with energy above threshold
|
||||
* @NL802154_CCA_ALOHA: CCA shall always report an idle medium
|
||||
* @NL802154_CCA_UWB_SHR: UWB preamble sense based on the SHR of a frame
|
||||
* @NL802154_CCA_UWB_MULTIPEXED: UWB preamble sense based on the packet with
|
||||
* @NL802154_CCA_UWB_MULTIPLEXED: UWB preamble sense based on the packet with
|
||||
* the multiplexed preamble
|
||||
* @__NL802154_CCA_ATTR_AFTER_LAST: Internal
|
||||
* @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number
|
||||
@ -197,7 +199,7 @@ enum nl802154_cca_modes {
|
||||
NL802154_CCA_ENERGY_CARRIER,
|
||||
NL802154_CCA_ALOHA,
|
||||
NL802154_CCA_UWB_SHR,
|
||||
NL802154_CCA_UWB_MULTIPEXED,
|
||||
NL802154_CCA_UWB_MULTIPLEXED,
|
||||
|
||||
/* keep last */
|
||||
__NL802154_CCA_ATTR_AFTER_LAST,
|
||||
|
@ -856,7 +856,7 @@ static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev)
|
||||
set_dev_addr(netdev, &chan->src, chan->src_type);
|
||||
|
||||
netdev->netdev_ops = &netdev_ops;
|
||||
SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev);
|
||||
SET_NETDEV_DEV(netdev, &chan->conn->hcon->hdev->dev);
|
||||
SET_NETDEV_DEVTYPE(netdev, &bt_type);
|
||||
|
||||
err = register_netdev(netdev);
|
||||
@ -928,7 +928,7 @@ static void delete_netdev(struct work_struct *work)
|
||||
|
||||
unregister_netdev(entry->netdev);
|
||||
|
||||
/* The entry pointer is deleted in device_event() */
|
||||
/* The entry pointer is deleted by the netdev destructor. */
|
||||
}
|
||||
|
||||
static void chan_close_cb(struct l2cap_chan *chan)
|
||||
@ -937,7 +937,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
||||
struct lowpan_dev *dev = NULL;
|
||||
struct lowpan_peer *peer;
|
||||
int err = -ENOENT;
|
||||
bool last = false, removed = true;
|
||||
bool last = false, remove = true;
|
||||
|
||||
BT_DBG("chan %p conn %p", chan, chan->conn);
|
||||
|
||||
@ -948,7 +948,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
||||
/* If conn is set, then the netdev is also there and we should
|
||||
* not remove it.
|
||||
*/
|
||||
removed = false;
|
||||
remove = false;
|
||||
}
|
||||
|
||||
spin_lock(&devices_lock);
|
||||
@ -977,7 +977,7 @@ static void chan_close_cb(struct l2cap_chan *chan)
|
||||
|
||||
ifdown(dev->netdev);
|
||||
|
||||
if (!removed) {
|
||||
if (remove) {
|
||||
INIT_WORK(&entry->delete_netdev, delete_netdev);
|
||||
schedule_work(&entry->delete_netdev);
|
||||
}
|
||||
@ -1208,8 +1208,6 @@ static void disconnect_all_peers(void)
|
||||
|
||||
list_del_rcu(&peer->list);
|
||||
kfree_rcu(peer, rcu);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
spin_unlock(&devices_lock);
|
||||
}
|
||||
@ -1418,7 +1416,6 @@ static int device_event(struct notifier_block *unused,
|
||||
BT_DBG("Unregistered netdev %s %p",
|
||||
netdev->name, netdev);
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,10 @@ obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o
|
||||
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 sco.o lib.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
|
||||
|
||||
bluetooth-$(CONFIG_BT_BREDR) += sco.o
|
||||
bluetooth-$(CONFIG_BT_DEBUGFS) += hci_debugfs.o
|
||||
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
|
||||
|
||||
|
@ -276,7 +276,7 @@ u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
|
||||
}
|
||||
|
||||
void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
__u8 ltk[16])
|
||||
__u8 ltk[16], __u8 key_size)
|
||||
{
|
||||
struct hci_dev *hdev = conn->hdev;
|
||||
struct hci_cp_le_start_enc cp;
|
||||
@ -288,7 +288,7 @@ void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand,
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
cp.rand = rand;
|
||||
cp.ediv = ediv;
|
||||
memcpy(cp.ltk, ltk, sizeof(cp.ltk));
|
||||
memcpy(cp.ltk, ltk, key_size);
|
||||
|
||||
hci_send_cmd(hdev, HCI_OP_LE_START_ENC, sizeof(cp), &cp);
|
||||
}
|
||||
|
@ -1553,6 +1553,7 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||
BT_DBG("%s %p", hdev->name, hdev);
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_UNREGISTER) &&
|
||||
!hci_dev_test_flag(hdev, HCI_USER_CHANNEL) &&
|
||||
test_bit(HCI_UP, &hdev->flags)) {
|
||||
/* Execute vendor specific shutdown routine */
|
||||
if (hdev->shutdown)
|
||||
@ -1590,6 +1591,11 @@ static int hci_dev_do_close(struct hci_dev *hdev)
|
||||
if (hci_dev_test_flag(hdev, HCI_MGMT))
|
||||
cancel_delayed_work_sync(&hdev->rpa_expired);
|
||||
|
||||
if (hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work_sync(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
/* Avoid potential lockdep warnings from the *_flush() calls by
|
||||
* ensuring the workqueue is empty up front.
|
||||
*/
|
||||
@ -2146,6 +2152,17 @@ static void hci_discov_off(struct work_struct *work)
|
||||
mgmt_discoverable_timeout(hdev);
|
||||
}
|
||||
|
||||
static void hci_adv_timeout_expire(struct work_struct *work)
|
||||
{
|
||||
struct hci_dev *hdev;
|
||||
|
||||
hdev = container_of(work, struct hci_dev, adv_instance_expire.work);
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
mgmt_adv_timeout_expired(hdev);
|
||||
}
|
||||
|
||||
void hci_uuids_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct bt_uuid *uuid, *tmp;
|
||||
@ -2609,6 +2626,130 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
struct adv_info *hci_find_adv_instance(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
|
||||
if (adv_instance->instance == instance)
|
||||
return adv_instance;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
struct adv_info *hci_get_next_instance(struct hci_dev *hdev, u8 instance) {
|
||||
struct adv_info *cur_instance;
|
||||
|
||||
cur_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!cur_instance)
|
||||
return NULL;
|
||||
|
||||
if (cur_instance == list_last_entry(&hdev->adv_instances,
|
||||
struct adv_info, list))
|
||||
return list_first_entry(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
else
|
||||
return list_next_entry(cur_instance, list);
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_remove_adv_instance(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return -ENOENT;
|
||||
|
||||
BT_DBG("%s removing %dMR", hdev->name, instance);
|
||||
|
||||
if (hdev->cur_adv_instance == instance && hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
list_del(&adv_instance->list);
|
||||
kfree(adv_instance);
|
||||
|
||||
hdev->adv_instance_cnt--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
void hci_adv_instances_clear(struct hci_dev *hdev)
|
||||
{
|
||||
struct adv_info *adv_instance, *n;
|
||||
|
||||
if (hdev->adv_instance_timeout) {
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
hdev->adv_instance_timeout = 0;
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
|
||||
list_del(&adv_instance->list);
|
||||
kfree(adv_instance);
|
||||
}
|
||||
|
||||
hdev->adv_instance_cnt = 0;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
int hci_add_adv_instance(struct hci_dev *hdev, u8 instance, u32 flags,
|
||||
u16 adv_data_len, u8 *adv_data,
|
||||
u16 scan_rsp_len, u8 *scan_rsp_data,
|
||||
u16 timeout, u16 duration)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (adv_instance) {
|
||||
memset(adv_instance->adv_data, 0,
|
||||
sizeof(adv_instance->adv_data));
|
||||
memset(adv_instance->scan_rsp_data, 0,
|
||||
sizeof(adv_instance->scan_rsp_data));
|
||||
} else {
|
||||
if (hdev->adv_instance_cnt >= HCI_MAX_ADV_INSTANCES ||
|
||||
instance < 1 || instance > HCI_MAX_ADV_INSTANCES)
|
||||
return -EOVERFLOW;
|
||||
|
||||
adv_instance = kzalloc(sizeof(*adv_instance), GFP_KERNEL);
|
||||
if (!adv_instance)
|
||||
return -ENOMEM;
|
||||
|
||||
adv_instance->pending = true;
|
||||
adv_instance->instance = instance;
|
||||
list_add(&adv_instance->list, &hdev->adv_instances);
|
||||
hdev->adv_instance_cnt++;
|
||||
}
|
||||
|
||||
adv_instance->flags = flags;
|
||||
adv_instance->adv_data_len = adv_data_len;
|
||||
adv_instance->scan_rsp_len = scan_rsp_len;
|
||||
|
||||
if (adv_data_len)
|
||||
memcpy(adv_instance->adv_data, adv_data, adv_data_len);
|
||||
|
||||
if (scan_rsp_len)
|
||||
memcpy(adv_instance->scan_rsp_data,
|
||||
scan_rsp_data, scan_rsp_len);
|
||||
|
||||
adv_instance->timeout = timeout;
|
||||
adv_instance->remaining_time = timeout;
|
||||
|
||||
if (duration == 0)
|
||||
adv_instance->duration = HCI_DEFAULT_ADV_DURATION;
|
||||
else
|
||||
adv_instance->duration = duration;
|
||||
|
||||
BT_DBG("%s for %dMR", hdev->name, instance);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list,
|
||||
bdaddr_t *bdaddr, u8 type)
|
||||
{
|
||||
@ -3014,6 +3155,9 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
hdev->manufacturer = 0xffff; /* Default to internal use */
|
||||
hdev->inq_tx_power = HCI_TX_POWER_INVALID;
|
||||
hdev->adv_tx_power = HCI_TX_POWER_INVALID;
|
||||
hdev->adv_instance_cnt = 0;
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
hdev->adv_instance_timeout = 0;
|
||||
|
||||
hdev->sniff_max_interval = 800;
|
||||
hdev->sniff_min_interval = 80;
|
||||
@ -3055,6 +3199,7 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
INIT_LIST_HEAD(&hdev->pend_le_conns);
|
||||
INIT_LIST_HEAD(&hdev->pend_le_reports);
|
||||
INIT_LIST_HEAD(&hdev->conn_hash.list);
|
||||
INIT_LIST_HEAD(&hdev->adv_instances);
|
||||
|
||||
INIT_WORK(&hdev->rx_work, hci_rx_work);
|
||||
INIT_WORK(&hdev->cmd_work, hci_cmd_work);
|
||||
@ -3066,6 +3211,7 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work);
|
||||
INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work);
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance_expire, hci_adv_timeout_expire);
|
||||
|
||||
skb_queue_head_init(&hdev->rx_q);
|
||||
skb_queue_head_init(&hdev->cmd_q);
|
||||
@ -3077,7 +3223,6 @@ struct hci_dev *hci_alloc_dev(void)
|
||||
|
||||
hci_init_sysfs(hdev);
|
||||
discovery_init(hdev);
|
||||
adv_info_init(hdev);
|
||||
|
||||
return hdev;
|
||||
}
|
||||
@ -3248,6 +3393,7 @@ void hci_unregister_dev(struct hci_dev *hdev)
|
||||
hci_smp_ltks_clear(hdev);
|
||||
hci_smp_irks_clear(hdev);
|
||||
hci_remote_oob_data_clear(hdev);
|
||||
hci_adv_instances_clear(hdev);
|
||||
hci_bdaddr_list_clear(&hdev->le_white_list);
|
||||
hci_conn_params_clear_all(hdev);
|
||||
hci_discovery_filter_clear(hdev);
|
||||
|
@ -2603,6 +2603,63 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void read_enc_key_size_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode, struct sk_buff *skb)
|
||||
{
|
||||
const struct hci_rp_read_enc_key_size *rp;
|
||||
struct hci_conn *conn;
|
||||
u16 handle;
|
||||
|
||||
BT_DBG("%s status 0x%02x", hdev->name, status);
|
||||
|
||||
if (!skb || skb->len < sizeof(*rp)) {
|
||||
BT_ERR("%s invalid HCI Read Encryption Key Size response",
|
||||
hdev->name);
|
||||
return;
|
||||
}
|
||||
|
||||
rp = (void *)skb->data;
|
||||
handle = le16_to_cpu(rp->handle);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
conn = hci_conn_hash_lookup_handle(hdev, handle);
|
||||
if (!conn)
|
||||
goto unlock;
|
||||
|
||||
/* If we fail to read the encryption key size, assume maximum
|
||||
* (which is the same we do also when this HCI command isn't
|
||||
* supported.
|
||||
*/
|
||||
if (rp->status) {
|
||||
BT_ERR("%s failed to read key size for handle %u", hdev->name,
|
||||
handle);
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
} else {
|
||||
conn->enc_key_size = rp->key_size;
|
||||
}
|
||||
|
||||
if (conn->state == BT_CONFIG) {
|
||||
conn->state = BT_CONNECTED;
|
||||
hci_connect_cfm(conn, 0);
|
||||
hci_conn_drop(conn);
|
||||
} else {
|
||||
u8 encrypt;
|
||||
|
||||
if (!test_bit(HCI_CONN_ENCRYPT, &conn->flags))
|
||||
encrypt = 0x00;
|
||||
else if (test_bit(HCI_CONN_AES_CCM, &conn->flags))
|
||||
encrypt = 0x02;
|
||||
else
|
||||
encrypt = 0x01;
|
||||
|
||||
hci_encrypt_cfm(conn, 0, encrypt);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
{
|
||||
struct hci_ev_encrypt_change *ev = (void *) skb->data;
|
||||
@ -2650,22 +2707,51 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* In Secure Connections Only mode, do not allow any connections
|
||||
* that are not encrypted with AES-CCM using a P-256 authenticated
|
||||
* combination key.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_SC_ONLY) &&
|
||||
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
|
||||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
|
||||
hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
/* Try reading the encryption key size for encrypted ACL links */
|
||||
if (!ev->status && ev->encrypt && conn->type == ACL_LINK) {
|
||||
struct hci_cp_read_enc_key_size cp;
|
||||
struct hci_request req;
|
||||
|
||||
/* Only send HCI_Read_Encryption_Key_Size if the
|
||||
* controller really supports it. If it doesn't, assume
|
||||
* the default size (16).
|
||||
*/
|
||||
if (!(hdev->commands[20] & 0x10)) {
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
goto notify;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
hci_req_add(&req, HCI_OP_READ_ENC_KEY_SIZE, sizeof(cp), &cp);
|
||||
|
||||
if (hci_req_run_skb(&req, read_enc_key_size_complete)) {
|
||||
BT_ERR("Sending HCI Read Encryption Key Size failed");
|
||||
conn->enc_key_size = HCI_LINK_KEY_SIZE;
|
||||
goto notify;
|
||||
}
|
||||
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
notify:
|
||||
if (conn->state == BT_CONFIG) {
|
||||
if (!ev->status)
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
/* In Secure Connections Only mode, do not allow any
|
||||
* connections that are not encrypted with AES-CCM
|
||||
* using a P-256 authenticated combination key.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_SC_ONLY) &&
|
||||
(!test_bit(HCI_CONN_AES_CCM, &conn->flags) ||
|
||||
conn->key_type != HCI_LK_AUTH_COMBINATION_P256)) {
|
||||
hci_connect_cfm(conn, HCI_ERROR_AUTH_FAILURE);
|
||||
hci_conn_drop(conn);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_connect_cfm(conn, ev->status);
|
||||
hci_conn_drop(conn);
|
||||
} else
|
||||
@ -4955,7 +5041,8 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
||||
goto not_found;
|
||||
}
|
||||
|
||||
memcpy(cp.ltk, ltk->val, sizeof(ltk->val));
|
||||
memcpy(cp.ltk, ltk->val, ltk->enc_size);
|
||||
memset(cp.ltk + ltk->enc_size, 0, sizeof(cp.ltk) - ltk->enc_size);
|
||||
cp.handle = cpu_to_le16(conn->handle);
|
||||
|
||||
conn->pending_sec_level = smp_ltk_sec_level(ltk);
|
||||
|
@ -503,9 +503,9 @@ static int hci_sock_release(struct socket *sock)
|
||||
|
||||
if (hdev) {
|
||||
if (hci_pi(sk)->channel == HCI_CHANNEL_USER) {
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
hci_dev_close(hdev->id);
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
mgmt_index_added(hdev);
|
||||
}
|
||||
|
||||
atomic_dec(&hdev->promisc);
|
||||
@ -741,10 +741,11 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (test_bit(HCI_UP, &hdev->flags) ||
|
||||
test_bit(HCI_INIT, &hdev->flags) ||
|
||||
if (test_bit(HCI_INIT, &hdev->flags) ||
|
||||
hci_dev_test_flag(hdev, HCI_SETUP) ||
|
||||
hci_dev_test_flag(hdev, HCI_CONFIG)) {
|
||||
hci_dev_test_flag(hdev, HCI_CONFIG) ||
|
||||
(!hci_dev_test_flag(hdev, HCI_AUTO_OFF) &&
|
||||
test_bit(HCI_UP, &hdev->flags))) {
|
||||
err = -EBUSY;
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
@ -760,10 +761,21 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr,
|
||||
|
||||
err = hci_dev_open(hdev->id);
|
||||
if (err) {
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
if (err == -EALREADY) {
|
||||
/* In case the transport is already up and
|
||||
* running, clear the error here.
|
||||
*
|
||||
* This can happen when opening an user
|
||||
* channel and HCI_AUTO_OFF grace period
|
||||
* is still active.
|
||||
*/
|
||||
err = 0;
|
||||
} else {
|
||||
hci_dev_clear_flag(hdev, HCI_USER_CHANNEL);
|
||||
mgmt_index_added(hdev);
|
||||
hci_dev_put(hdev);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
atomic_inc(&hdev->promisc);
|
||||
|
@ -1601,7 +1601,7 @@ int l2cap_register_user(struct l2cap_conn *conn, struct l2cap_user *user)
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (user->list.next || user->list.prev) {
|
||||
if (!list_empty(&user->list)) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
@ -1631,12 +1631,10 @@ void l2cap_unregister_user(struct l2cap_conn *conn, struct l2cap_user *user)
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (!user->list.next || !user->list.prev)
|
||||
if (list_empty(&user->list))
|
||||
goto out_unlock;
|
||||
|
||||
list_del(&user->list);
|
||||
user->list.next = NULL;
|
||||
user->list.prev = NULL;
|
||||
user->remove(conn, user);
|
||||
|
||||
out_unlock:
|
||||
@ -1651,8 +1649,6 @@ static void l2cap_unregister_all_users(struct l2cap_conn *conn)
|
||||
while (!list_empty(&conn->users)) {
|
||||
user = list_first_entry(&conn->users, struct l2cap_user, list);
|
||||
list_del(&user->list);
|
||||
user->list.next = NULL;
|
||||
user->list.prev = NULL;
|
||||
user->remove(conn, user);
|
||||
}
|
||||
}
|
||||
@ -7442,7 +7438,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
|
||||
mutex_unlock(&conn->chan_lock);
|
||||
}
|
||||
|
||||
int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
void l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
{
|
||||
struct l2cap_conn *conn = hcon->l2cap_data;
|
||||
struct l2cap_hdr *hdr;
|
||||
@ -7485,7 +7481,7 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
if (len == skb->len) {
|
||||
/* Complete frame received */
|
||||
l2cap_recv_frame(conn, skb);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
BT_DBG("Start: total len %d, frag len %d", len, skb->len);
|
||||
@ -7544,7 +7540,6 @@ int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 flags)
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hci_cb l2cap_cb = {
|
||||
|
@ -38,7 +38,7 @@
|
||||
#include "mgmt_util.h"
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
#define MGMT_REVISION 9
|
||||
#define MGMT_REVISION 10
|
||||
|
||||
static const u16 mgmt_commands[] = {
|
||||
MGMT_OP_READ_INDEX_LIST,
|
||||
@ -832,6 +832,20 @@ static struct mgmt_pending_cmd *pending_find_data(u16 opcode,
|
||||
return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data);
|
||||
}
|
||||
|
||||
static u8 get_current_adv_instance(struct hci_dev *hdev)
|
||||
{
|
||||
/* The "Set Advertising" setting supersedes the "Add Advertising"
|
||||
* setting. Here we set the advertising data based on which
|
||||
* setting was set. When neither apply, default to the global settings,
|
||||
* represented by instance "0".
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return hdev->cur_adv_instance;
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
|
||||
{
|
||||
u8 ad_len = 0;
|
||||
@ -858,19 +872,25 @@ static u8 create_default_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
|
||||
return ad_len;
|
||||
}
|
||||
|
||||
static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 *ptr)
|
||||
static u8 create_instance_scan_rsp_data(struct hci_dev *hdev, u8 instance,
|
||||
u8 *ptr)
|
||||
{
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
/* TODO: Set the appropriate entries based on advertising instance flags
|
||||
* here once flags other than 0 are supported.
|
||||
*/
|
||||
memcpy(ptr, hdev->adv_instance.scan_rsp_data,
|
||||
hdev->adv_instance.scan_rsp_len);
|
||||
memcpy(ptr, adv_instance->scan_rsp_data,
|
||||
adv_instance->scan_rsp_len);
|
||||
|
||||
return hdev->adv_instance.scan_rsp_len;
|
||||
return adv_instance->scan_rsp_len;
|
||||
}
|
||||
|
||||
static void update_scan_rsp_data_for_instance(struct hci_request *req,
|
||||
u8 instance)
|
||||
static void update_inst_scan_rsp_data(struct hci_request *req, u8 instance)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_cp_le_set_scan_rsp_data cp;
|
||||
@ -882,7 +902,7 @@ static void update_scan_rsp_data_for_instance(struct hci_request *req,
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
|
||||
if (instance)
|
||||
len = create_instance_scan_rsp_data(hdev, cp.data);
|
||||
len = create_instance_scan_rsp_data(hdev, instance, cp.data);
|
||||
else
|
||||
len = create_default_scan_rsp_data(hdev, cp.data);
|
||||
|
||||
@ -900,21 +920,7 @@ static void update_scan_rsp_data_for_instance(struct hci_request *req,
|
||||
|
||||
static void update_scan_rsp_data(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 instance;
|
||||
|
||||
/* The "Set Advertising" setting supersedes the "Add Advertising"
|
||||
* setting. Here we set the scan response data based on which
|
||||
* setting was set. When neither apply, default to the global settings,
|
||||
* represented by instance "0".
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
instance = 0x01;
|
||||
else
|
||||
instance = 0x00;
|
||||
|
||||
update_scan_rsp_data_for_instance(req, instance);
|
||||
update_inst_scan_rsp_data(req, get_current_adv_instance(req->hdev));
|
||||
}
|
||||
|
||||
static u8 get_adv_discov_flags(struct hci_dev *hdev)
|
||||
@ -941,20 +947,6 @@ static u8 get_adv_discov_flags(struct hci_dev *hdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 get_current_adv_instance(struct hci_dev *hdev)
|
||||
{
|
||||
/* The "Set Advertising" setting supersedes the "Add Advertising"
|
||||
* setting. Here we set the advertising data based on which
|
||||
* setting was set. When neither apply, default to the global settings,
|
||||
* represented by instance "0".
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return 0x01;
|
||||
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static bool get_connectable(struct hci_dev *hdev)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
@ -975,41 +967,65 @@ static bool get_connectable(struct hci_dev *hdev)
|
||||
static u32 get_adv_instance_flags(struct hci_dev *hdev, u8 instance)
|
||||
{
|
||||
u32 flags;
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
if (instance > 0x01)
|
||||
if (instance == 0x00) {
|
||||
/* Instance 0 always manages the "Tx Power" and "Flags"
|
||||
* fields
|
||||
*/
|
||||
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
|
||||
|
||||
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting
|
||||
* corresponds to the "connectable" instance flag.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
|
||||
flags |= MGMT_ADV_FLAG_CONNECTABLE;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
/* Return 0 when we got an invalid instance identifier. */
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
if (instance == 0x01)
|
||||
return hdev->adv_instance.flags;
|
||||
|
||||
/* Instance 0 always manages the "Tx Power" and "Flags" fields */
|
||||
flags = MGMT_ADV_FLAG_TX_POWER | MGMT_ADV_FLAG_MANAGED_FLAGS;
|
||||
|
||||
/* For instance 0, the HCI_ADVERTISING_CONNECTABLE setting corresponds
|
||||
* to the "connectable" instance flag.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE))
|
||||
flags |= MGMT_ADV_FLAG_CONNECTABLE;
|
||||
|
||||
return flags;
|
||||
return adv_instance->flags;
|
||||
}
|
||||
|
||||
static u8 get_adv_instance_scan_rsp_len(struct hci_dev *hdev, u8 instance)
|
||||
static u8 get_cur_adv_instance_scan_rsp_len(struct hci_dev *hdev)
|
||||
{
|
||||
/* Ignore instance 0 and other unsupported instances */
|
||||
if (instance != 0x01)
|
||||
u8 instance = get_current_adv_instance(hdev);
|
||||
struct adv_info *adv_instance;
|
||||
|
||||
/* Ignore instance 0 */
|
||||
if (instance == 0x00)
|
||||
return 0;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
|
||||
/* TODO: Take into account the "appearance" and "local-name" flags here.
|
||||
* These are currently being ignored as they are not supported.
|
||||
*/
|
||||
return hdev->adv_instance.scan_rsp_len;
|
||||
return adv_instance->scan_rsp_len;
|
||||
}
|
||||
|
||||
static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
{
|
||||
struct adv_info *adv_instance = NULL;
|
||||
u8 ad_len = 0, flags = 0;
|
||||
u32 instance_flags = get_adv_instance_flags(hdev, instance);
|
||||
u32 instance_flags;
|
||||
|
||||
/* Return 0 when the current instance identifier is invalid. */
|
||||
if (instance) {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return 0;
|
||||
}
|
||||
|
||||
instance_flags = get_adv_instance_flags(hdev, instance);
|
||||
|
||||
/* The Add Advertising command allows userspace to set both the general
|
||||
* and limited discoverable flags.
|
||||
@ -1043,12 +1059,11 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
}
|
||||
}
|
||||
|
||||
if (instance) {
|
||||
memcpy(ptr, hdev->adv_instance.adv_data,
|
||||
hdev->adv_instance.adv_data_len);
|
||||
|
||||
ad_len += hdev->adv_instance.adv_data_len;
|
||||
ptr += hdev->adv_instance.adv_data_len;
|
||||
if (adv_instance) {
|
||||
memcpy(ptr, adv_instance->adv_data,
|
||||
adv_instance->adv_data_len);
|
||||
ad_len += adv_instance->adv_data_len;
|
||||
ptr += adv_instance->adv_data_len;
|
||||
}
|
||||
|
||||
/* Provide Tx Power only if we can provide a valid value for it */
|
||||
@ -1065,7 +1080,7 @@ static u8 create_instance_adv_data(struct hci_dev *hdev, u8 instance, u8 *ptr)
|
||||
return ad_len;
|
||||
}
|
||||
|
||||
static void update_adv_data_for_instance(struct hci_request *req, u8 instance)
|
||||
static void update_inst_adv_data(struct hci_request *req, u8 instance)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_cp_le_set_adv_data cp;
|
||||
@ -1093,10 +1108,7 @@ static void update_adv_data_for_instance(struct hci_request *req, u8 instance)
|
||||
|
||||
static void update_adv_data(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 instance = get_current_adv_instance(hdev);
|
||||
|
||||
update_adv_data_for_instance(req, instance);
|
||||
update_inst_adv_data(req, get_current_adv_instance(req->hdev));
|
||||
}
|
||||
|
||||
int mgmt_update_adv_data(struct hci_dev *hdev)
|
||||
@ -1277,7 +1289,7 @@ static void enable_advertising(struct hci_request *req)
|
||||
|
||||
if (connectable)
|
||||
cp.type = LE_ADV_IND;
|
||||
else if (get_adv_instance_scan_rsp_len(hdev, instance))
|
||||
else if (get_cur_adv_instance_scan_rsp_len(hdev))
|
||||
cp.type = LE_ADV_SCAN_IND;
|
||||
else
|
||||
cp.type = LE_ADV_NONCONN_IND;
|
||||
@ -1459,27 +1471,141 @@ static void advertising_removed(struct sock *sk, struct hci_dev *hdev,
|
||||
mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk);
|
||||
}
|
||||
|
||||
static void clear_adv_instance(struct hci_dev *hdev)
|
||||
static int schedule_adv_instance(struct hci_request *req, u8 instance,
|
||||
bool force) {
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct adv_info *adv_instance = NULL;
|
||||
u16 timeout;
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
return -EPERM;
|
||||
|
||||
if (hdev->adv_instance_timeout)
|
||||
return -EBUSY;
|
||||
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
if (!adv_instance)
|
||||
return -ENOENT;
|
||||
|
||||
/* A zero timeout means unlimited advertising. As long as there is
|
||||
* only one instance, duration should be ignored. We still set a timeout
|
||||
* in case further instances are being added later on.
|
||||
*
|
||||
* If the remaining lifetime of the instance is more than the duration
|
||||
* then the timeout corresponds to the duration, otherwise it will be
|
||||
* reduced to the remaining instance lifetime.
|
||||
*/
|
||||
if (adv_instance->timeout == 0 ||
|
||||
adv_instance->duration <= adv_instance->remaining_time)
|
||||
timeout = adv_instance->duration;
|
||||
else
|
||||
timeout = adv_instance->remaining_time;
|
||||
|
||||
/* The remaining time is being reduced unless the instance is being
|
||||
* advertised without time limit.
|
||||
*/
|
||||
if (adv_instance->timeout)
|
||||
adv_instance->remaining_time =
|
||||
adv_instance->remaining_time - timeout;
|
||||
|
||||
hdev->adv_instance_timeout = timeout;
|
||||
queue_delayed_work(hdev->workqueue,
|
||||
&hdev->adv_instance_expire,
|
||||
msecs_to_jiffies(timeout * 1000));
|
||||
|
||||
/* If we're just re-scheduling the same instance again then do not
|
||||
* execute any HCI commands. This happens when a single instance is
|
||||
* being advertised.
|
||||
*/
|
||||
if (!force && hdev->cur_adv_instance == instance &&
|
||||
hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
return 0;
|
||||
|
||||
hdev->cur_adv_instance = instance;
|
||||
update_adv_data(req);
|
||||
update_scan_rsp_data(req);
|
||||
enable_advertising(req);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cancel_adv_timeout(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
if (hdev->adv_instance_timeout) {
|
||||
hdev->adv_instance_timeout = 0;
|
||||
cancel_delayed_work(&hdev->adv_instance_expire);
|
||||
}
|
||||
}
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
return;
|
||||
/* For a single instance:
|
||||
* - force == true: The instance will be removed even when its remaining
|
||||
* lifetime is not zero.
|
||||
* - force == false: the instance will be deactivated but kept stored unless
|
||||
* the remaining lifetime is zero.
|
||||
*
|
||||
* For instance == 0x00:
|
||||
* - force == true: All instances will be removed regardless of their timeout
|
||||
* setting.
|
||||
* - force == false: Only instances that have a timeout will be removed.
|
||||
*/
|
||||
static void clear_adv_instance(struct hci_dev *hdev, struct hci_request *req,
|
||||
u8 instance, bool force)
|
||||
{
|
||||
struct adv_info *adv_instance, *n, *next_instance = NULL;
|
||||
int err;
|
||||
u8 rem_inst;
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
cancel_delayed_work(&hdev->adv_instance.timeout_exp);
|
||||
/* Cancel any timeout concerning the removed instance(s). */
|
||||
if (!instance || hdev->cur_adv_instance == instance)
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
|
||||
advertising_removed(NULL, hdev, 1);
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
/* Get the next instance to advertise BEFORE we remove
|
||||
* the current one. This can be the same instance again
|
||||
* if there is only one instance.
|
||||
*/
|
||||
if (instance && hdev->cur_adv_instance == instance)
|
||||
next_instance = hci_get_next_instance(hdev, instance);
|
||||
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
if (instance == 0x00) {
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances,
|
||||
list) {
|
||||
if (!(force || adv_instance->timeout))
|
||||
continue;
|
||||
|
||||
rem_inst = adv_instance->instance;
|
||||
err = hci_remove_adv_instance(hdev, rem_inst);
|
||||
if (!err)
|
||||
advertising_removed(NULL, hdev, rem_inst);
|
||||
}
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
} else {
|
||||
adv_instance = hci_find_adv_instance(hdev, instance);
|
||||
|
||||
if (force || (adv_instance && adv_instance->timeout &&
|
||||
!adv_instance->remaining_time)) {
|
||||
/* Don't advertise a removed instance. */
|
||||
if (next_instance &&
|
||||
next_instance->instance == instance)
|
||||
next_instance = NULL;
|
||||
|
||||
err = hci_remove_adv_instance(hdev, instance);
|
||||
if (!err)
|
||||
advertising_removed(NULL, hdev, instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (list_empty(&hdev->adv_instances)) {
|
||||
hdev->cur_adv_instance = 0x00;
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
}
|
||||
|
||||
if (!req || !hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
return;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
disable_advertising(&req);
|
||||
hci_req_run(&req, NULL);
|
||||
if (next_instance)
|
||||
schedule_adv_instance(req, next_instance->instance, false);
|
||||
}
|
||||
|
||||
static int clean_up_hci_state(struct hci_dev *hdev)
|
||||
@ -1497,8 +1623,7 @@ static int clean_up_hci_state(struct hci_dev *hdev)
|
||||
hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
|
||||
}
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
clear_adv_instance(hdev);
|
||||
clear_adv_instance(hdev, NULL, 0x00, false);
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
||||
disable_advertising(&req);
|
||||
@ -2453,6 +2578,9 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
||||
val = !!cp->val;
|
||||
enabled = lmp_host_le_capable(hdev);
|
||||
|
||||
if (!val)
|
||||
clear_adv_instance(hdev, NULL, 0x00, true);
|
||||
|
||||
if (!hdev_is_powered(hdev) || val == enabled) {
|
||||
bool changed = false;
|
||||
|
||||
@ -4087,6 +4215,7 @@ static bool trigger_le_scan(struct hci_request *req, u16 interval, u8 *status)
|
||||
return false;
|
||||
}
|
||||
|
||||
cancel_adv_timeout(hdev);
|
||||
disable_advertising(req);
|
||||
}
|
||||
|
||||
@ -4669,6 +4798,9 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
{
|
||||
struct cmd_lookup match = { NULL, hdev };
|
||||
struct hci_request req;
|
||||
u8 instance;
|
||||
struct adv_info *adv_instance;
|
||||
int err;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
@ -4694,18 +4826,31 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
sock_put(match.sk);
|
||||
|
||||
/* If "Set Advertising" was just disabled and instance advertising was
|
||||
* set up earlier, then enable the advertising instance.
|
||||
* set up earlier, then re-enable multi-instance advertising.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) ||
|
||||
list_empty(&hdev->adv_instances))
|
||||
goto unlock;
|
||||
|
||||
instance = hdev->cur_adv_instance;
|
||||
if (!instance) {
|
||||
adv_instance = list_first_entry_or_null(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
if (!adv_instance)
|
||||
goto unlock;
|
||||
|
||||
instance = adv_instance->instance;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
update_adv_data(&req);
|
||||
enable_advertising(&req);
|
||||
err = schedule_adv_instance(&req, instance, true);
|
||||
|
||||
if (hci_req_run(&req, enable_advertising_instance) < 0)
|
||||
if (!err)
|
||||
err = hci_req_run(&req, enable_advertising_instance);
|
||||
|
||||
if (err)
|
||||
BT_ERR("Failed to re-configure advertising");
|
||||
|
||||
unlock:
|
||||
@ -4790,10 +4935,15 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
|
||||
else
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE);
|
||||
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
if (val) {
|
||||
/* Switch to instance "0" for the Set Advertising setting. */
|
||||
update_adv_data_for_instance(&req, 0);
|
||||
update_scan_rsp_data_for_instance(&req, 0);
|
||||
/* Switch to instance "0" for the Set Advertising setting.
|
||||
* We cannot use update_[adv|scan_rsp]_data() here as the
|
||||
* HCI_ADVERTISING flag is not yet set.
|
||||
*/
|
||||
update_inst_adv_data(&req, 0x00);
|
||||
update_inst_scan_rsp_data(&req, 0x00);
|
||||
enable_advertising(&req);
|
||||
} else {
|
||||
disable_advertising(&req);
|
||||
@ -6781,8 +6931,9 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
{
|
||||
struct mgmt_rp_read_adv_features *rp;
|
||||
size_t rp_len;
|
||||
int err;
|
||||
int err, i;
|
||||
bool instance;
|
||||
struct adv_info *adv_instance;
|
||||
u32 supported_flags;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
@ -6795,12 +6946,9 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
rp_len = sizeof(*rp);
|
||||
|
||||
/* Currently only one instance is supported, so just add 1 to the
|
||||
* response length.
|
||||
*/
|
||||
instance = hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
if (instance)
|
||||
rp_len++;
|
||||
rp_len += hdev->adv_instance_cnt;
|
||||
|
||||
rp = kmalloc(rp_len, GFP_ATOMIC);
|
||||
if (!rp) {
|
||||
@ -6813,14 +6961,18 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
|
||||
rp->supported_flags = cpu_to_le32(supported_flags);
|
||||
rp->max_adv_data_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH;
|
||||
rp->max_instances = 1;
|
||||
rp->max_instances = HCI_MAX_ADV_INSTANCES;
|
||||
|
||||
/* Currently only one instance is supported, so simply return the
|
||||
* current instance number.
|
||||
*/
|
||||
if (instance) {
|
||||
rp->num_instances = 1;
|
||||
rp->instance[0] = 1;
|
||||
i = 0;
|
||||
list_for_each_entry(adv_instance, &hdev->adv_instances, list) {
|
||||
if (i >= hdev->adv_instance_cnt)
|
||||
break;
|
||||
|
||||
rp->instance[i] = adv_instance->instance;
|
||||
i++;
|
||||
}
|
||||
rp->num_instances = hdev->adv_instance_cnt;
|
||||
} else {
|
||||
rp->num_instances = 0;
|
||||
}
|
||||
@ -6882,7 +7034,10 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct mgmt_cp_add_advertising *cp;
|
||||
struct mgmt_rp_add_advertising rp;
|
||||
struct adv_info *adv_instance, *n;
|
||||
u8 instance;
|
||||
|
||||
BT_DBG("status %d", status);
|
||||
|
||||
@ -6890,16 +7045,32 @@ static void add_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
|
||||
cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev);
|
||||
|
||||
if (status) {
|
||||
if (status)
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
|
||||
advertising_removed(cmd ? cmd->sk : NULL, hdev, 1);
|
||||
|
||||
list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) {
|
||||
if (!adv_instance->pending)
|
||||
continue;
|
||||
|
||||
if (!status) {
|
||||
adv_instance->pending = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
instance = adv_instance->instance;
|
||||
|
||||
if (hdev->cur_adv_instance == instance)
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
hci_remove_adv_instance(hdev, instance);
|
||||
advertising_removed(cmd ? cmd->sk : NULL, hdev, instance);
|
||||
}
|
||||
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
rp.instance = 0x01;
|
||||
cp = cmd->param;
|
||||
rp.instance = cp->instance;
|
||||
|
||||
if (status)
|
||||
mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode,
|
||||
@ -6914,15 +7085,28 @@ unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static void adv_timeout_expired(struct work_struct *work)
|
||||
void mgmt_adv_timeout_expired(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_dev *hdev = container_of(work, struct hci_dev,
|
||||
adv_instance.timeout_exp.work);
|
||||
u8 instance;
|
||||
struct hci_request req;
|
||||
|
||||
hdev->adv_instance.timeout = 0;
|
||||
hdev->adv_instance_timeout = 0;
|
||||
|
||||
instance = get_current_adv_instance(hdev);
|
||||
if (instance == 0x00)
|
||||
return;
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
clear_adv_instance(hdev);
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
clear_adv_instance(hdev, &req, instance, false);
|
||||
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
disable_advertising(&req);
|
||||
|
||||
if (!skb_queue_empty(&req.cmd_q))
|
||||
hci_req_run(&req, NULL);
|
||||
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
@ -6934,7 +7118,10 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
u32 flags;
|
||||
u32 supported_flags;
|
||||
u8 status;
|
||||
u16 timeout;
|
||||
u16 timeout, duration;
|
||||
unsigned int prev_instance_cnt = hdev->adv_instance_cnt;
|
||||
u8 schedule_instance = 0;
|
||||
struct adv_info *next_instance;
|
||||
int err;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
@ -6948,12 +7135,13 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
flags = __le32_to_cpu(cp->flags);
|
||||
timeout = __le16_to_cpu(cp->timeout);
|
||||
duration = __le16_to_cpu(cp->duration);
|
||||
|
||||
/* The current implementation only supports adding one instance and only
|
||||
* a subset of the specified flags.
|
||||
/* The current implementation only supports a subset of the specified
|
||||
* flags.
|
||||
*/
|
||||
supported_flags = get_supported_adv_flags(hdev);
|
||||
if (cp->instance != 0x01 || (flags & ~supported_flags))
|
||||
if (flags & ~supported_flags)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
@ -6981,38 +7169,51 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
INIT_DELAYED_WORK(&hdev->adv_instance.timeout_exp, adv_timeout_expired);
|
||||
err = hci_add_adv_instance(hdev, cp->instance, flags,
|
||||
cp->adv_data_len, cp->data,
|
||||
cp->scan_rsp_len,
|
||||
cp->data + cp->adv_data_len,
|
||||
timeout, duration);
|
||||
if (err < 0) {
|
||||
err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_FAILED);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hdev->adv_instance.flags = flags;
|
||||
hdev->adv_instance.adv_data_len = cp->adv_data_len;
|
||||
hdev->adv_instance.scan_rsp_len = cp->scan_rsp_len;
|
||||
/* Only trigger an advertising added event if a new instance was
|
||||
* actually added.
|
||||
*/
|
||||
if (hdev->adv_instance_cnt > prev_instance_cnt)
|
||||
advertising_added(sk, hdev, cp->instance);
|
||||
|
||||
if (cp->adv_data_len)
|
||||
memcpy(hdev->adv_instance.adv_data, cp->data, cp->adv_data_len);
|
||||
hci_dev_set_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
|
||||
if (cp->scan_rsp_len)
|
||||
memcpy(hdev->adv_instance.scan_rsp_data,
|
||||
cp->data + cp->adv_data_len, cp->scan_rsp_len);
|
||||
if (hdev->cur_adv_instance == cp->instance) {
|
||||
/* If the currently advertised instance is being changed then
|
||||
* cancel the current advertising and schedule the next
|
||||
* instance. If there is only one instance then the overridden
|
||||
* advertising data will be visible right away.
|
||||
*/
|
||||
cancel_adv_timeout(hdev);
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
cancel_delayed_work(&hdev->adv_instance.timeout_exp);
|
||||
next_instance = hci_get_next_instance(hdev, cp->instance);
|
||||
if (next_instance)
|
||||
schedule_instance = next_instance->instance;
|
||||
} else if (!hdev->adv_instance_timeout) {
|
||||
/* Immediately advertise the new instance if no other
|
||||
* instance is currently being advertised.
|
||||
*/
|
||||
schedule_instance = cp->instance;
|
||||
}
|
||||
|
||||
hdev->adv_instance.timeout = timeout;
|
||||
|
||||
if (timeout)
|
||||
queue_delayed_work(hdev->workqueue,
|
||||
&hdev->adv_instance.timeout_exp,
|
||||
msecs_to_jiffies(timeout * 1000));
|
||||
|
||||
if (!hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
advertising_added(sk, hdev, 1);
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered then
|
||||
* we have no HCI communication to make. Simply return.
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered or
|
||||
* there is no instance to be advertised then we have no HCI
|
||||
* communication to make. Simply return.
|
||||
*/
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
|
||||
rp.instance = 0x01;
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!schedule_instance) {
|
||||
rp.instance = cp->instance;
|
||||
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
|
||||
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
|
||||
goto unlock;
|
||||
@ -7030,11 +7231,11 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
enable_advertising(&req);
|
||||
err = schedule_adv_instance(&req, schedule_instance, true);
|
||||
|
||||
if (!err)
|
||||
err = hci_req_run(&req, add_advertising_complete);
|
||||
|
||||
err = hci_req_run(&req, add_advertising_complete);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
@ -7048,6 +7249,7 @@ static void remove_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
u16 opcode)
|
||||
{
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct mgmt_cp_remove_advertising *cp;
|
||||
struct mgmt_rp_remove_advertising rp;
|
||||
|
||||
BT_DBG("status %d", status);
|
||||
@ -7062,7 +7264,8 @@ static void remove_advertising_complete(struct hci_dev *hdev, u8 status,
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
rp.instance = 1;
|
||||
cp = cmd->param;
|
||||
rp.instance = cp->instance;
|
||||
|
||||
mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, MGMT_STATUS_SUCCESS,
|
||||
&rp, sizeof(rp));
|
||||
@ -7077,21 +7280,21 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
{
|
||||
struct mgmt_cp_remove_advertising *cp = data;
|
||||
struct mgmt_rp_remove_advertising rp;
|
||||
int err;
|
||||
struct mgmt_pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
/* The current implementation only allows modifying instance no 1. A
|
||||
* value of 0 indicates that all instances should be cleared.
|
||||
*/
|
||||
if (cp->instance > 1)
|
||||
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
if (cp->instance && !hci_find_adv_instance(hdev, cp->instance)) {
|
||||
err = mgmt_cmd_status(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_ADVERTISING,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) ||
|
||||
pending_find(MGMT_OP_SET_LE, hdev)) {
|
||||
@ -7106,21 +7309,21 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (hdev->adv_instance.timeout)
|
||||
cancel_delayed_work(&hdev->adv_instance.timeout_exp);
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
memset(&hdev->adv_instance, 0, sizeof(hdev->adv_instance));
|
||||
clear_adv_instance(hdev, &req, cp->instance, true);
|
||||
|
||||
advertising_removed(sk, hdev, 1);
|
||||
if (list_empty(&hdev->adv_instances))
|
||||
disable_advertising(&req);
|
||||
|
||||
hci_dev_clear_flag(hdev, HCI_ADVERTISING_INSTANCE);
|
||||
|
||||
/* If the HCI_ADVERTISING flag is set or the device isn't powered then
|
||||
* we have no HCI communication to make. Simply return.
|
||||
/* If no HCI commands have been collected so far or the HCI_ADVERTISING
|
||||
* flag is set or the device isn't powered then we have no HCI
|
||||
* communication to make. Simply return.
|
||||
*/
|
||||
if (!hdev_is_powered(hdev) ||
|
||||
if (skb_queue_empty(&req.cmd_q) ||
|
||||
!hdev_is_powered(hdev) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING)) {
|
||||
rp.instance = 1;
|
||||
rp.instance = cp->instance;
|
||||
err = mgmt_cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_ADVERTISING,
|
||||
MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
|
||||
@ -7134,9 +7337,6 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
disable_advertising(&req);
|
||||
|
||||
err = hci_req_run(&req, remove_advertising_complete);
|
||||
if (err < 0)
|
||||
mgmt_pending_remove(cmd);
|
||||
@ -7361,6 +7561,7 @@ static void powered_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
static int powered_update_hci(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
struct adv_info *adv_instance;
|
||||
u8 link_sec;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
@ -7400,14 +7601,27 @@ static int powered_update_hci(struct hci_dev *hdev)
|
||||
* advertising data. This also applies to the case
|
||||
* where BR/EDR was toggled during the AUTO_OFF phase.
|
||||
*/
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
|
||||
if (hci_dev_test_flag(hdev, HCI_LE_ENABLED) &&
|
||||
(hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))) {
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
|
||||
hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
hdev->cur_adv_instance == 0x00 &&
|
||||
!list_empty(&hdev->adv_instances)) {
|
||||
adv_instance = list_first_entry(&hdev->adv_instances,
|
||||
struct adv_info, list);
|
||||
hdev->cur_adv_instance = adv_instance->instance;
|
||||
}
|
||||
|
||||
if (hci_dev_test_flag(hdev, HCI_ADVERTISING))
|
||||
enable_advertising(&req);
|
||||
else if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE) &&
|
||||
hdev->cur_adv_instance)
|
||||
schedule_adv_instance(&req, hdev->cur_adv_instance,
|
||||
true);
|
||||
|
||||
restart_le_actions(&req);
|
||||
}
|
||||
@ -7603,7 +7817,12 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent)
|
||||
if (key->type == SMP_LTK)
|
||||
ev.key.master = 1;
|
||||
|
||||
memcpy(ev.key.val, key->val, sizeof(key->val));
|
||||
/* Make sure we copy only the significant bytes based on the
|
||||
* encryption key size, and set the rest of the value to zeroes.
|
||||
*/
|
||||
memcpy(ev.key.val, key->val, sizeof(key->enc_size));
|
||||
memset(ev.key.val + key->enc_size, 0,
|
||||
sizeof(ev.key.val) - key->enc_size);
|
||||
|
||||
mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL);
|
||||
}
|
||||
@ -8387,13 +8606,24 @@ static void adv_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
||||
void mgmt_reenable_advertising(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
u8 instance;
|
||||
|
||||
if (!hci_dev_test_flag(hdev, HCI_ADVERTISING) &&
|
||||
!hci_dev_test_flag(hdev, HCI_ADVERTISING_INSTANCE))
|
||||
return;
|
||||
|
||||
instance = get_current_adv_instance(hdev);
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
enable_advertising(&req);
|
||||
|
||||
if (instance) {
|
||||
schedule_adv_instance(&req, instance, true);
|
||||
} else {
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
enable_advertising(&req);
|
||||
}
|
||||
|
||||
hci_req_run(&req, adv_enable_complete);
|
||||
}
|
||||
|
||||
|
@ -334,16 +334,19 @@ static int rfcomm_sock_create(struct net *net, struct socket *sock,
|
||||
|
||||
static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
|
||||
{
|
||||
struct sockaddr_rc *sa = (struct sockaddr_rc *) addr;
|
||||
struct sockaddr_rc sa;
|
||||
struct sock *sk = sock->sk;
|
||||
int chan = sa->rc_channel;
|
||||
int err = 0;
|
||||
|
||||
BT_DBG("sk %p %pMR", sk, &sa->rc_bdaddr);
|
||||
int len, err = 0;
|
||||
|
||||
if (!addr || addr->sa_family != AF_BLUETOOTH)
|
||||
return -EINVAL;
|
||||
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
len = min_t(unsigned int, sizeof(sa), addr_len);
|
||||
memcpy(&sa, addr, len);
|
||||
|
||||
BT_DBG("sk %p %pMR", sk, &sa.rc_bdaddr);
|
||||
|
||||
lock_sock(sk);
|
||||
|
||||
if (sk->sk_state != BT_OPEN) {
|
||||
@ -358,12 +361,13 @@ static int rfcomm_sock_bind(struct socket *sock, struct sockaddr *addr, int addr
|
||||
|
||||
write_lock(&rfcomm_sk_list.lock);
|
||||
|
||||
if (chan && __rfcomm_get_listen_sock_by_addr(chan, &sa->rc_bdaddr)) {
|
||||
if (sa.rc_channel &&
|
||||
__rfcomm_get_listen_sock_by_addr(sa.rc_channel, &sa.rc_bdaddr)) {
|
||||
err = -EADDRINUSE;
|
||||
} else {
|
||||
/* Save source address */
|
||||
bacpy(&rfcomm_pi(sk)->src, &sa->rc_bdaddr);
|
||||
rfcomm_pi(sk)->channel = chan;
|
||||
bacpy(&rfcomm_pi(sk)->src, &sa.rc_bdaddr);
|
||||
rfcomm_pi(sk)->channel = sa.rc_channel;
|
||||
sk->sk_state = BT_BOUND;
|
||||
}
|
||||
|
||||
|
@ -1110,7 +1110,7 @@ static void sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
|
||||
sco_conn_del(hcon, bt_to_errno(reason));
|
||||
}
|
||||
|
||||
int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
void sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
{
|
||||
struct sco_conn *conn = hcon->sco_data;
|
||||
|
||||
@ -1121,12 +1121,11 @@ int sco_recv_scodata(struct hci_conn *hcon, struct sk_buff *skb)
|
||||
|
||||
if (skb->len) {
|
||||
sco_recv_frame(conn, skb);
|
||||
return 0;
|
||||
return;
|
||||
}
|
||||
|
||||
drop:
|
||||
kfree_skb(skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct hci_cb sco_cb = {
|
||||
|
@ -33,6 +33,9 @@
|
||||
#include "ecc.h"
|
||||
#include "smp.h"
|
||||
|
||||
#define SMP_DEV(hdev) \
|
||||
((struct smp_dev *)((struct l2cap_chan *)((hdev)->smp_data))->data)
|
||||
|
||||
/* Low-level debug macros to be used for stuff that we don't want
|
||||
* accidentially in dmesg, i.e. the values of the various crypto keys
|
||||
* and the inputs & outputs of crypto functions.
|
||||
@ -81,6 +84,9 @@ struct smp_dev {
|
||||
u8 local_rand[16];
|
||||
bool debug_key;
|
||||
|
||||
u8 min_key_size;
|
||||
u8 max_key_size;
|
||||
|
||||
struct crypto_blkcipher *tfm_aes;
|
||||
struct crypto_hash *tfm_cmac;
|
||||
};
|
||||
@ -708,7 +714,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
if (rsp == NULL) {
|
||||
req->io_capability = conn->hcon->io_capability;
|
||||
req->oob_flag = oob_flag;
|
||||
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
req->max_key_size = SMP_DEV(hdev)->max_key_size;
|
||||
req->init_key_dist = local_dist;
|
||||
req->resp_key_dist = remote_dist;
|
||||
req->auth_req = (authreq & AUTH_REQ_MASK(hdev));
|
||||
@ -719,7 +725,7 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
|
||||
rsp->io_capability = conn->hcon->io_capability;
|
||||
rsp->oob_flag = oob_flag;
|
||||
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
rsp->max_key_size = SMP_DEV(hdev)->max_key_size;
|
||||
rsp->init_key_dist = req->init_key_dist & remote_dist;
|
||||
rsp->resp_key_dist = req->resp_key_dist & local_dist;
|
||||
rsp->auth_req = (authreq & AUTH_REQ_MASK(hdev));
|
||||
@ -730,10 +736,11 @@ static void build_pairing_cmd(struct l2cap_conn *conn,
|
||||
static u8 check_enc_key_size(struct l2cap_conn *conn, __u8 max_key_size)
|
||||
{
|
||||
struct l2cap_chan *chan = conn->smp;
|
||||
struct hci_dev *hdev = conn->hcon->hdev;
|
||||
struct smp_chan *smp = chan->data;
|
||||
|
||||
if ((max_key_size > SMP_MAX_ENC_KEY_SIZE) ||
|
||||
(max_key_size < SMP_MIN_ENC_KEY_SIZE))
|
||||
if (max_key_size > SMP_DEV(hdev)->max_key_size ||
|
||||
max_key_size < SMP_MIN_ENC_KEY_SIZE)
|
||||
return SMP_ENC_KEY_SIZE;
|
||||
|
||||
smp->enc_key_size = max_key_size;
|
||||
@ -997,13 +1004,10 @@ static u8 smp_random(struct smp_chan *smp)
|
||||
|
||||
smp_s1(smp->tfm_aes, smp->tk, smp->rrnd, smp->prnd, stk);
|
||||
|
||||
memset(stk + smp->enc_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
|
||||
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
|
||||
return SMP_UNSPECIFIED;
|
||||
|
||||
hci_le_start_enc(hcon, ediv, rand, stk);
|
||||
hci_le_start_enc(hcon, ediv, rand, stk, smp->enc_key_size);
|
||||
hcon->enc_key_size = smp->enc_key_size;
|
||||
set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags);
|
||||
} else {
|
||||
@ -1016,9 +1020,6 @@ static u8 smp_random(struct smp_chan *smp)
|
||||
|
||||
smp_s1(smp->tfm_aes, smp->tk, smp->prnd, smp->rrnd, stk);
|
||||
|
||||
memset(stk + smp->enc_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
|
||||
|
||||
if (hcon->pending_sec_level == BT_SECURITY_HIGH)
|
||||
auth = 1;
|
||||
else
|
||||
@ -1156,9 +1157,6 @@ static void sc_add_ltk(struct smp_chan *smp)
|
||||
else
|
||||
auth = 0;
|
||||
|
||||
memset(smp->tk + smp->enc_key_size, 0,
|
||||
SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size);
|
||||
|
||||
smp->ltk = hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type,
|
||||
key_type, auth, smp->tk, smp->enc_key_size,
|
||||
0, 0);
|
||||
@ -1280,7 +1278,14 @@ static void smp_distribute_keys(struct smp_chan *smp)
|
||||
__le16 ediv;
|
||||
__le64 rand;
|
||||
|
||||
get_random_bytes(enc.ltk, sizeof(enc.ltk));
|
||||
/* Make sure we generate only the significant amount of
|
||||
* bytes based on the encryption key size, and set the rest
|
||||
* of the value to zeroes.
|
||||
*/
|
||||
get_random_bytes(enc.ltk, smp->enc_key_size);
|
||||
memset(enc.ltk + smp->enc_key_size, 0,
|
||||
sizeof(enc.ltk) - smp->enc_key_size);
|
||||
|
||||
get_random_bytes(&ediv, sizeof(ediv));
|
||||
get_random_bytes(&rand, sizeof(rand));
|
||||
|
||||
@ -1700,7 +1705,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp,
|
||||
|
||||
req->init_key_dist = local_dist;
|
||||
req->resp_key_dist = remote_dist;
|
||||
req->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
req->max_key_size = conn->hcon->enc_key_size;
|
||||
|
||||
smp->remote_key_dist = remote_dist;
|
||||
|
||||
@ -1709,7 +1714,7 @@ static void build_bredr_pairing_cmd(struct smp_chan *smp,
|
||||
|
||||
memset(rsp, 0, sizeof(*rsp));
|
||||
|
||||
rsp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
rsp->max_key_size = conn->hcon->enc_key_size;
|
||||
rsp->init_key_dist = req->init_key_dist & remote_dist;
|
||||
rsp->resp_key_dist = req->resp_key_dist & local_dist;
|
||||
|
||||
@ -2202,7 +2207,7 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level)
|
||||
if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags))
|
||||
return true;
|
||||
|
||||
hci_le_start_enc(hcon, key->ediv, key->rand, key->val);
|
||||
hci_le_start_enc(hcon, key->ediv, key->rand, key->val, key->enc_size);
|
||||
hcon->enc_key_size = key->enc_size;
|
||||
|
||||
/* We never store STKs for master role, so clear this flag */
|
||||
@ -2750,7 +2755,7 @@ static int smp_cmd_dhkey_check(struct l2cap_conn *conn, struct sk_buff *skb)
|
||||
sc_add_ltk(smp);
|
||||
|
||||
if (hcon->out) {
|
||||
hci_le_start_enc(hcon, 0, 0, smp->tk);
|
||||
hci_le_start_enc(hcon, 0, 0, smp->tk, smp->enc_key_size);
|
||||
hcon->enc_key_size = smp->enc_key_size;
|
||||
}
|
||||
|
||||
@ -3132,6 +3137,8 @@ static struct l2cap_chan *smp_add_cid(struct hci_dev *hdev, u16 cid)
|
||||
|
||||
smp->tfm_aes = tfm_aes;
|
||||
smp->tfm_cmac = tfm_cmac;
|
||||
smp->min_key_size = SMP_MIN_ENC_KEY_SIZE;
|
||||
smp->max_key_size = SMP_MAX_ENC_KEY_SIZE;
|
||||
|
||||
create_chan:
|
||||
chan = l2cap_chan_create();
|
||||
@ -3254,6 +3261,94 @@ static const struct file_operations force_bredr_smp_fops = {
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t le_min_key_size_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[4];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%2u\n", SMP_DEV(hdev)->min_key_size);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t le_min_key_size_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
u8 key_size;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
|
||||
sscanf(buf, "%hhu", &key_size);
|
||||
|
||||
if (key_size > SMP_DEV(hdev)->max_key_size ||
|
||||
key_size < SMP_MIN_ENC_KEY_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
SMP_DEV(hdev)->min_key_size = key_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations le_min_key_size_fops = {
|
||||
.open = simple_open,
|
||||
.read = le_min_key_size_read,
|
||||
.write = le_min_key_size_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t le_max_key_size_read(struct file *file,
|
||||
char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[4];
|
||||
|
||||
snprintf(buf, sizeof(buf), "%2u\n", SMP_DEV(hdev)->max_key_size);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf));
|
||||
}
|
||||
|
||||
static ssize_t le_max_key_size_write(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct hci_dev *hdev = file->private_data;
|
||||
char buf[32];
|
||||
size_t buf_size = min(count, (sizeof(buf) - 1));
|
||||
u8 key_size;
|
||||
|
||||
if (copy_from_user(buf, user_buf, buf_size))
|
||||
return -EFAULT;
|
||||
|
||||
buf[buf_size] = '\0';
|
||||
|
||||
sscanf(buf, "%hhu", &key_size);
|
||||
|
||||
if (key_size > SMP_MAX_ENC_KEY_SIZE ||
|
||||
key_size < SMP_DEV(hdev)->min_key_size)
|
||||
return -EINVAL;
|
||||
|
||||
SMP_DEV(hdev)->max_key_size = key_size;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static const struct file_operations le_max_key_size_fops = {
|
||||
.open = simple_open,
|
||||
.read = le_max_key_size_read,
|
||||
.write = le_max_key_size_write,
|
||||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
int smp_register(struct hci_dev *hdev)
|
||||
{
|
||||
struct l2cap_chan *chan;
|
||||
@ -3278,6 +3373,11 @@ int smp_register(struct hci_dev *hdev)
|
||||
|
||||
hdev->smp_data = chan;
|
||||
|
||||
debugfs_create_file("le_min_key_size", 0644, hdev->debugfs, hdev,
|
||||
&le_min_key_size_fops);
|
||||
debugfs_create_file("le_max_key_size", 0644, hdev->debugfs, hdev,
|
||||
&le_max_key_size_fops);
|
||||
|
||||
/* If the controller does not support BR/EDR Secure Connections
|
||||
* feature, then the BR/EDR SMP channel shall not be present.
|
||||
*
|
||||
|
@ -190,6 +190,7 @@ err:
|
||||
|
||||
static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct wpan_dev *wpan_dev = lowpan_dev_info(dev)->real_dev->ieee802154_ptr;
|
||||
struct ieee802154_addr sa, da;
|
||||
struct ieee802154_mac_cb *cb = mac_cb_init(skb);
|
||||
struct lowpan_addr_info info;
|
||||
@ -207,7 +208,7 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
|
||||
|
||||
/* prepare wpan address data */
|
||||
sa.mode = IEEE802154_ADDR_LONG;
|
||||
sa.pan_id = lowpan_dev_info(dev)->real_dev->ieee802154_ptr->pan_id;
|
||||
sa.pan_id = wpan_dev->pan_id;
|
||||
sa.extended_addr = ieee802154_devaddr_from_raw(saddr);
|
||||
|
||||
/* intra-PAN communications */
|
||||
@ -223,7 +224,7 @@ static int lowpan_header(struct sk_buff *skb, struct net_device *dev)
|
||||
} else {
|
||||
da.mode = IEEE802154_ADDR_LONG;
|
||||
da.extended_addr = ieee802154_devaddr_from_raw(daddr);
|
||||
cb->ackreq = true;
|
||||
cb->ackreq = wpan_dev->frame_retries >= 0;
|
||||
}
|
||||
|
||||
return dev_hard_header(skb, lowpan_dev_info(dev)->real_dev,
|
||||
|
@ -228,6 +228,8 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
|
||||
[NL802154_ATTR_LBT_MODE] = { .type = NLA_U8, },
|
||||
|
||||
[NL802154_ATTR_WPAN_PHY_CAPS] = { .type = NLA_NESTED },
|
||||
|
||||
[NL802154_ATTR_SUPPORTED_COMMANDS] = { .type = NLA_NESTED },
|
||||
};
|
||||
|
||||
/* message building helper */
|
||||
@ -372,7 +374,9 @@ static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
|
||||
struct sk_buff *msg, u32 portid, u32 seq,
|
||||
int flags)
|
||||
{
|
||||
struct nlattr *nl_cmds;
|
||||
void *hdr;
|
||||
int i;
|
||||
|
||||
hdr = nl802154hdr_put(msg, portid, seq, flags, cmd);
|
||||
if (!hdr)
|
||||
@ -431,6 +435,42 @@ static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
|
||||
if (nl802154_put_capabilities(msg, rdev))
|
||||
goto nla_put_failure;
|
||||
|
||||
nl_cmds = nla_nest_start(msg, NL802154_ATTR_SUPPORTED_COMMANDS);
|
||||
if (!nl_cmds)
|
||||
goto nla_put_failure;
|
||||
|
||||
i = 0;
|
||||
#define CMD(op, n) \
|
||||
do { \
|
||||
if (rdev->ops->op) { \
|
||||
i++; \
|
||||
if (nla_put_u32(msg, i, NL802154_CMD_ ## n)) \
|
||||
goto nla_put_failure; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
CMD(add_virtual_intf, NEW_INTERFACE);
|
||||
CMD(del_virtual_intf, DEL_INTERFACE);
|
||||
CMD(set_channel, SET_CHANNEL);
|
||||
CMD(set_pan_id, SET_PAN_ID);
|
||||
CMD(set_short_addr, SET_SHORT_ADDR);
|
||||
CMD(set_backoff_exponent, SET_BACKOFF_EXPONENT);
|
||||
CMD(set_max_csma_backoffs, SET_MAX_CSMA_BACKOFFS);
|
||||
CMD(set_max_frame_retries, SET_MAX_FRAME_RETRIES);
|
||||
CMD(set_lbt_mode, SET_LBT_MODE);
|
||||
|
||||
if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_TXPOWER)
|
||||
CMD(set_tx_power, SET_TX_POWER);
|
||||
|
||||
if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_ED_LEVEL)
|
||||
CMD(set_cca_ed_level, SET_CCA_ED_LEVEL);
|
||||
|
||||
if (rdev->wpan_phy.flags & WPAN_PHY_FLAG_CCA_MODE)
|
||||
CMD(set_cca_mode, SET_CCA_MODE);
|
||||
|
||||
#undef CMD
|
||||
nla_nest_end(msg, nl_cmds);
|
||||
|
||||
finish:
|
||||
genlmsg_end(msg, hdr);
|
||||
return 0;
|
||||
|
@ -731,6 +731,12 @@ static int dgram_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
|
||||
sock_recv_ts_and_drops(msg, sk, skb);
|
||||
|
||||
if (saddr) {
|
||||
/* Clear the implicit padding in struct sockaddr_ieee802154
|
||||
* (16 bits between 'family' and 'addr') and in struct
|
||||
* ieee802154_addr_sa (16 bits at the end of the structure).
|
||||
*/
|
||||
memset(saddr, 0, sizeof(*saddr));
|
||||
|
||||
saddr->family = AF_IEEE802154;
|
||||
ieee802154_addr_to_sa(&saddr->addr, &mac_cb(skb)->source);
|
||||
*addr_len = sizeof(*saddr);
|
||||
|
@ -56,7 +56,7 @@ TRACE_EVENT(802154_rdev_add_virtual_intf,
|
||||
__entry->type = type;
|
||||
__entry->extended_addr = extended_addr;
|
||||
),
|
||||
TP_printk(WPAN_PHY_PR_FMT ", virtual intf name: %s, type: %d, ea %llx",
|
||||
TP_printk(WPAN_PHY_PR_FMT ", virtual intf name: %s, type: %d, extended addr: 0x%llx",
|
||||
WPAN_PHY_PR_ARG, __get_str(vir_intf_name), __entry->type,
|
||||
__le64_to_cpu(__entry->extended_addr))
|
||||
);
|
||||
@ -104,7 +104,7 @@ TRACE_EVENT(802154_rdev_set_tx_power,
|
||||
WPAN_PHY_ASSIGN;
|
||||
__entry->power = power;
|
||||
),
|
||||
TP_printk(WPAN_PHY_PR_FMT ", power: %d", WPAN_PHY_PR_ARG,
|
||||
TP_printk(WPAN_PHY_PR_FMT ", mbm: %d", WPAN_PHY_PR_ARG,
|
||||
__entry->power)
|
||||
);
|
||||
|
||||
@ -134,7 +134,7 @@ TRACE_EVENT(802154_rdev_set_cca_ed_level,
|
||||
WPAN_PHY_ASSIGN;
|
||||
__entry->ed_level = ed_level;
|
||||
),
|
||||
TP_printk(WPAN_PHY_PR_FMT ", ed_level: %d", WPAN_PHY_PR_ARG,
|
||||
TP_printk(WPAN_PHY_PR_FMT ", ed level: %d", WPAN_PHY_PR_ARG,
|
||||
__entry->ed_level)
|
||||
);
|
||||
|
||||
@ -167,7 +167,7 @@ DEFINE_EVENT_PRINT(802154_le16_template, 802154_rdev_set_short_addr,
|
||||
TP_PROTO(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
__le16 le16arg),
|
||||
TP_ARGS(wpan_phy, wpan_dev, le16arg),
|
||||
TP_printk(WPAN_PHY_PR_FMT ", " WPAN_DEV_PR_FMT ", sa: 0x%04x",
|
||||
TP_printk(WPAN_PHY_PR_FMT ", " WPAN_DEV_PR_FMT ", short addr: 0x%04x",
|
||||
WPAN_PHY_PR_ARG, WPAN_DEV_PR_ARG,
|
||||
__le16_to_cpu(__entry->le16arg))
|
||||
);
|
||||
@ -190,7 +190,7 @@ TRACE_EVENT(802154_rdev_set_backoff_exponent,
|
||||
),
|
||||
|
||||
TP_printk(WPAN_PHY_PR_FMT ", " WPAN_DEV_PR_FMT
|
||||
", min be: %d, max_be: %d", WPAN_PHY_PR_ARG,
|
||||
", min be: %d, max be: %d", WPAN_PHY_PR_ARG,
|
||||
WPAN_DEV_PR_ARG, __entry->min_be, __entry->max_be)
|
||||
);
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
obj-$(CONFIG_MAC802154) += mac802154.o
|
||||
mac802154-objs := main.o rx.o tx.o mac_cmd.o mib.o \
|
||||
iface.o llsec.o util.o cfg.o
|
||||
iface.o llsec.o util.o cfg.o trace.o
|
||||
|
||||
CFLAGS_trace.o := -I$(src)
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <net/mac802154.h>
|
||||
|
||||
#include "ieee802154_i.h"
|
||||
#include "trace.h"
|
||||
|
||||
static inline int
|
||||
drv_xmit_async(struct ieee802154_local *local, struct sk_buff *skb)
|
||||
@ -27,19 +28,25 @@ drv_xmit_sync(struct ieee802154_local *local, struct sk_buff *skb)
|
||||
|
||||
static inline int drv_start(struct ieee802154_local *local)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
trace_802154_drv_start(local);
|
||||
local->started = true;
|
||||
smp_mb();
|
||||
|
||||
return local->ops->start(&local->hw);
|
||||
ret = local->ops->start(&local->hw);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void drv_stop(struct ieee802154_local *local)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
trace_802154_drv_stop(local);
|
||||
local->ops->stop(&local->hw);
|
||||
trace_802154_drv_return_void(local);
|
||||
|
||||
/* sync away all work on the tasklet before clearing started */
|
||||
tasklet_disable(&local->tasklet);
|
||||
@ -53,13 +60,20 @@ static inline void drv_stop(struct ieee802154_local *local)
|
||||
static inline int
|
||||
drv_set_channel(struct ieee802154_local *local, u8 page, u8 channel)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
return local->ops->set_channel(&local->hw, page, channel);
|
||||
trace_802154_drv_set_channel(local, page, channel);
|
||||
ret = local->ops->set_channel(&local->hw, page, channel);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_set_tx_power(struct ieee802154_local *local, s32 mbm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_txpower) {
|
||||
@ -67,12 +81,17 @@ static inline int drv_set_tx_power(struct ieee802154_local *local, s32 mbm)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_txpower(&local->hw, mbm);
|
||||
trace_802154_drv_set_tx_power(local, mbm);
|
||||
ret = local->ops->set_txpower(&local->hw, mbm);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_set_cca_mode(struct ieee802154_local *local,
|
||||
const struct wpan_phy_cca *cca)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_cca_mode) {
|
||||
@ -80,11 +99,16 @@ static inline int drv_set_cca_mode(struct ieee802154_local *local,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_cca_mode(&local->hw, cca);
|
||||
trace_802154_drv_set_cca_mode(local, cca);
|
||||
ret = local->ops->set_cca_mode(&local->hw, cca);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_lbt) {
|
||||
@ -92,12 +116,17 @@ static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_lbt(&local->hw, mode);
|
||||
trace_802154_drv_set_lbt_mode(local, mode);
|
||||
ret = local->ops->set_lbt(&local->hw, mode);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_cca_ed_level(struct ieee802154_local *local, s32 mbm)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_cca_ed_level) {
|
||||
@ -105,12 +134,16 @@ drv_set_cca_ed_level(struct ieee802154_local *local, s32 mbm)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_cca_ed_level(&local->hw, mbm);
|
||||
trace_802154_drv_set_cca_ed_level(local, mbm);
|
||||
ret = local->ops->set_cca_ed_level(&local->hw, mbm);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
@ -121,14 +154,18 @@ static inline int drv_set_pan_id(struct ieee802154_local *local, __le16 pan_id)
|
||||
|
||||
filt.pan_id = pan_id;
|
||||
|
||||
return local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
trace_802154_drv_set_pan_id(local, pan_id);
|
||||
ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
IEEE802154_AFILT_PANID_CHANGED);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
@ -139,14 +176,18 @@ drv_set_extended_addr(struct ieee802154_local *local, __le64 extended_addr)
|
||||
|
||||
filt.ieee_addr = extended_addr;
|
||||
|
||||
return local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
trace_802154_drv_set_extended_addr(local, extended_addr);
|
||||
ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
IEEE802154_AFILT_IEEEADDR_CHANGED);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
@ -157,14 +198,18 @@ drv_set_short_addr(struct ieee802154_local *local, __le16 short_addr)
|
||||
|
||||
filt.short_addr = short_addr;
|
||||
|
||||
return local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
trace_802154_drv_set_short_addr(local, short_addr);
|
||||
ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
IEEE802154_AFILT_SADDR_CHANGED);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
|
||||
{
|
||||
struct ieee802154_hw_addr_filt filt;
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
@ -175,14 +220,19 @@ drv_set_pan_coord(struct ieee802154_local *local, bool is_coord)
|
||||
|
||||
filt.pan_coord = is_coord;
|
||||
|
||||
return local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
trace_802154_drv_set_pan_coord(local, is_coord);
|
||||
ret = local->ops->set_hw_addr_filt(&local->hw, &filt,
|
||||
IEEE802154_AFILT_PANC_CHANGED);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
|
||||
u8 max_csma_backoffs)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_csma_params) {
|
||||
@ -190,13 +240,19 @@ drv_set_csma_params(struct ieee802154_local *local, u8 min_be, u8 max_be,
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_csma_params(&local->hw, min_be, max_be,
|
||||
trace_802154_drv_set_csma_params(local, min_be, max_be,
|
||||
max_csma_backoffs);
|
||||
ret = local->ops->set_csma_params(&local->hw, min_be, max_be,
|
||||
max_csma_backoffs);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_frame_retries) {
|
||||
@ -204,12 +260,17 @@ drv_set_max_frame_retries(struct ieee802154_local *local, s8 max_frame_retries)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_frame_retries(&local->hw, max_frame_retries);
|
||||
trace_802154_drv_set_max_frame_retries(local, max_frame_retries);
|
||||
ret = local->ops->set_frame_retries(&local->hw, max_frame_retries);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline int
|
||||
drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
|
||||
{
|
||||
int ret;
|
||||
|
||||
might_sleep();
|
||||
|
||||
if (!local->ops->set_promiscuous_mode) {
|
||||
@ -217,7 +278,10 @@ drv_set_promiscuous_mode(struct ieee802154_local *local, bool on)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_promiscuous_mode(&local->hw, on);
|
||||
trace_802154_drv_set_promiscuous_mode(local, on);
|
||||
ret = local->ops->set_promiscuous_mode(&local->hw, on);
|
||||
trace_802154_drv_return_int(local, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* __MAC802154_DRIVER_OPS */
|
||||
|
@ -92,8 +92,6 @@ struct ieee802154_sub_if_data {
|
||||
struct mutex sec_mtx;
|
||||
|
||||
struct mac802154_llsec sec;
|
||||
/* must be last, dynamically sized area in this! */
|
||||
struct ieee802154_vif vif;
|
||||
};
|
||||
|
||||
#define MAC802154_CHAN_NONE 0xff /* No channel is assigned */
|
||||
|
@ -126,7 +126,7 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
|
||||
return -EBUSY;
|
||||
|
||||
ieee802154_be64_to_le64(&extended_addr, addr->sa_data);
|
||||
if (!ieee802154_is_valid_extended_addr(extended_addr))
|
||||
if (!ieee802154_is_valid_extended_unicast_addr(extended_addr))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
|
||||
@ -135,19 +135,72 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
|
||||
return mac802154_wpan_update_llsec(dev);
|
||||
}
|
||||
|
||||
static int ieee802154_setup_hw(struct ieee802154_sub_if_data *sdata)
|
||||
{
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
int ret;
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
|
||||
ret = drv_set_promiscuous_mode(local,
|
||||
wpan_dev->promiscuous_mode);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_AFILT) {
|
||||
ret = drv_set_pan_id(local, wpan_dev->pan_id);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = drv_set_extended_addr(local, wpan_dev->extended_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = drv_set_short_addr(local, wpan_dev->short_addr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_LBT) {
|
||||
ret = drv_set_lbt_mode(local, wpan_dev->lbt);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
|
||||
ret = drv_set_csma_params(local, wpan_dev->min_be,
|
||||
wpan_dev->max_be,
|
||||
wpan_dev->csma_retries);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
|
||||
ret = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac802154_slave_open(struct net_device *dev)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
int res = 0;
|
||||
int res;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
set_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
||||
if (!local->open_count) {
|
||||
res = ieee802154_setup_hw(sdata);
|
||||
if (res)
|
||||
goto err;
|
||||
|
||||
res = drv_start(local);
|
||||
WARN_ON(res);
|
||||
if (res)
|
||||
goto err;
|
||||
}
|
||||
@ -219,8 +272,8 @@ ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata,
|
||||
* exist really an use case if we need to support
|
||||
* multiple node types at the same time.
|
||||
*/
|
||||
if (sdata->vif.type == NL802154_IFTYPE_NODE &&
|
||||
nsdata->vif.type == NL802154_IFTYPE_NODE)
|
||||
if (wpan_dev->iftype == NL802154_IFTYPE_NODE &&
|
||||
nsdata->wpan_dev.iftype == NL802154_IFTYPE_NODE)
|
||||
return -EBUSY;
|
||||
|
||||
/* check all phy mac sublayer settings are the same.
|
||||
@ -240,60 +293,13 @@ static int mac802154_wpan_open(struct net_device *dev)
|
||||
{
|
||||
int rc;
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
|
||||
rc = ieee802154_check_concurrent_iface(sdata, sdata->vif.type);
|
||||
rc = ieee802154_check_concurrent_iface(sdata, wpan_dev->iftype);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = mac802154_slave_open(dev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
|
||||
rc = drv_set_promiscuous_mode(local,
|
||||
wpan_dev->promiscuous_mode);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_AFILT) {
|
||||
rc = drv_set_pan_id(local, wpan_dev->pan_id);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = drv_set_extended_addr(local, wpan_dev->extended_addr);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = drv_set_short_addr(local, wpan_dev->short_addr);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_LBT) {
|
||||
rc = drv_set_lbt_mode(local, wpan_dev->lbt);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
|
||||
rc = drv_set_csma_params(local, wpan_dev->min_be,
|
||||
wpan_dev->max_be,
|
||||
wpan_dev->csma_retries);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
|
||||
rc = drv_set_max_frame_retries(local, wpan_dev->frame_retries);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return rc;
|
||||
return mac802154_slave_open(dev);
|
||||
}
|
||||
|
||||
static int mac802154_slave_close(struct net_device *dev)
|
||||
@ -303,15 +309,16 @@ static int mac802154_slave_close(struct net_device *dev)
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
hrtimer_cancel(&local->ifs_timer);
|
||||
|
||||
netif_stop_queue(dev);
|
||||
local->open_count--;
|
||||
|
||||
clear_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
||||
if (!local->open_count)
|
||||
if (!local->open_count) {
|
||||
flush_workqueue(local->workqueue);
|
||||
hrtimer_cancel(&local->ifs_timer);
|
||||
drv_stop(local);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -467,7 +474,6 @@ ieee802154_setup_sdata(struct ieee802154_sub_if_data *sdata,
|
||||
u8 tmp;
|
||||
|
||||
/* set some type-dependent values */
|
||||
sdata->vif.type = type;
|
||||
sdata->wpan_dev.iftype = type;
|
||||
|
||||
get_random_bytes(&tmp, sizeof(tmp));
|
||||
@ -523,7 +529,7 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
ndev = alloc_netdev(sizeof(*sdata) + local->hw.vif_data_size, name,
|
||||
ndev = alloc_netdev(sizeof(*sdata), name,
|
||||
name_assign_type, ieee802154_if_setup);
|
||||
if (!ndev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -539,7 +545,7 @@ ieee802154_if_add(struct ieee802154_local *local, const char *name,
|
||||
switch (type) {
|
||||
case NL802154_IFTYPE_NODE:
|
||||
ndev->type = ARPHRD_IEEE802154;
|
||||
if (ieee802154_is_valid_extended_addr(extended_addr))
|
||||
if (ieee802154_is_valid_extended_unicast_addr(extended_addr))
|
||||
ieee802154_le64_to_be64(ndev->dev_addr, &extended_addr);
|
||||
else
|
||||
memcpy(ndev->dev_addr, ndev->perm_addr,
|
||||
|
@ -202,8 +202,10 @@ __ieee802154_rx_handle_packet(struct ieee802154_local *local,
|
||||
}
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != NL802154_IFTYPE_NODE ||
|
||||
!netif_running(sdata->dev))
|
||||
if (sdata->wpan_dev.iftype != NL802154_IFTYPE_NODE)
|
||||
continue;
|
||||
|
||||
if (!ieee802154_sdata_running(sdata))
|
||||
continue;
|
||||
|
||||
ieee802154_subif_frame(sdata, skb, &hdr);
|
||||
@ -227,7 +229,7 @@ ieee802154_monitors_rx(struct ieee802154_local *local, struct sk_buff *skb)
|
||||
skb->protocol = htons(ETH_P_IEEE802154);
|
||||
|
||||
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
|
||||
if (sdata->vif.type != NL802154_IFTYPE_MONITOR)
|
||||
if (sdata->wpan_dev.iftype != NL802154_IFTYPE_MONITOR)
|
||||
continue;
|
||||
|
||||
if (!ieee802154_sdata_running(sdata))
|
||||
|
9
net/mac802154/trace.c
Normal file
9
net/mac802154/trace.c
Normal file
@ -0,0 +1,9 @@
|
||||
#include <linux/module.h>
|
||||
|
||||
#ifndef __CHECKER__
|
||||
#include <net/cfg802154.h>
|
||||
#include "driver-ops.h"
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "trace.h"
|
||||
|
||||
#endif
|
272
net/mac802154/trace.h
Normal file
272
net/mac802154/trace.h
Normal file
@ -0,0 +1,272 @@
|
||||
/* Based on net/mac80211/trace.h */
|
||||
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM mac802154
|
||||
|
||||
#if !defined(__MAC802154_DRIVER_TRACE) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define __MAC802154_DRIVER_TRACE
|
||||
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
#include <net/mac802154.h>
|
||||
#include "ieee802154_i.h"
|
||||
|
||||
#define MAXNAME 32
|
||||
#define LOCAL_ENTRY __array(char, wpan_phy_name, MAXNAME)
|
||||
#define LOCAL_ASSIGN strlcpy(__entry->wpan_phy_name, \
|
||||
wpan_phy_name(local->hw.phy), MAXNAME)
|
||||
#define LOCAL_PR_FMT "%s"
|
||||
#define LOCAL_PR_ARG __entry->wpan_phy_name
|
||||
|
||||
#define CCA_ENTRY __field(enum nl802154_cca_modes, cca_mode) \
|
||||
__field(enum nl802154_cca_opts, cca_opt)
|
||||
#define CCA_ASSIGN \
|
||||
do { \
|
||||
(__entry->cca_mode) = cca->mode; \
|
||||
(__entry->cca_opt) = cca->opt; \
|
||||
} while (0)
|
||||
#define CCA_PR_FMT "cca_mode: %d, cca_opt: %d"
|
||||
#define CCA_PR_ARG __entry->cca_mode, __entry->cca_opt
|
||||
|
||||
#define BOOL_TO_STR(bo) (bo) ? "true" : "false"
|
||||
|
||||
/* Tracing for driver callbacks */
|
||||
|
||||
DECLARE_EVENT_CLASS(local_only_evt,
|
||||
TP_PROTO(struct ieee802154_local *local),
|
||||
TP_ARGS(local),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT, LOCAL_PR_ARG)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, 802154_drv_return_void,
|
||||
TP_PROTO(struct ieee802154_local *local),
|
||||
TP_ARGS(local)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_return_int,
|
||||
TP_PROTO(struct ieee802154_local *local, int ret),
|
||||
TP_ARGS(local, ret),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(int, ret)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->ret = ret;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", returned: %d", LOCAL_PR_ARG,
|
||||
__entry->ret)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, 802154_drv_start,
|
||||
TP_PROTO(struct ieee802154_local *local),
|
||||
TP_ARGS(local)
|
||||
);
|
||||
|
||||
DEFINE_EVENT(local_only_evt, 802154_drv_stop,
|
||||
TP_PROTO(struct ieee802154_local *local),
|
||||
TP_ARGS(local)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_channel,
|
||||
TP_PROTO(struct ieee802154_local *local, u8 page, u8 channel),
|
||||
TP_ARGS(local, page, channel),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(u8, page)
|
||||
__field(u8, channel)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->page = page;
|
||||
__entry->channel = channel;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", page: %d, channel: %d", LOCAL_PR_ARG,
|
||||
__entry->page, __entry->channel)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_cca_mode,
|
||||
TP_PROTO(struct ieee802154_local *local,
|
||||
const struct wpan_phy_cca *cca),
|
||||
TP_ARGS(local, cca),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
CCA_ENTRY
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
CCA_ASSIGN;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", " CCA_PR_FMT, LOCAL_PR_ARG,
|
||||
CCA_PR_ARG)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_cca_ed_level,
|
||||
TP_PROTO(struct ieee802154_local *local, s32 mbm),
|
||||
TP_ARGS(local, mbm),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(s32, mbm)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->mbm = mbm;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", ed level: %d", LOCAL_PR_ARG,
|
||||
__entry->mbm)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_tx_power,
|
||||
TP_PROTO(struct ieee802154_local *local, s32 power),
|
||||
TP_ARGS(local, power),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(s32, power)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->power = power;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", mbm: %d", LOCAL_PR_ARG,
|
||||
__entry->power)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_lbt_mode,
|
||||
TP_PROTO(struct ieee802154_local *local, bool mode),
|
||||
TP_ARGS(local, mode),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(bool, mode)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->mode = mode;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", lbt mode: %s", LOCAL_PR_ARG,
|
||||
BOOL_TO_STR(__entry->mode))
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_short_addr,
|
||||
TP_PROTO(struct ieee802154_local *local, __le16 short_addr),
|
||||
TP_ARGS(local, short_addr),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(__le16, short_addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->short_addr = short_addr;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", short addr: 0x%04x", LOCAL_PR_ARG,
|
||||
le16_to_cpu(__entry->short_addr))
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_pan_id,
|
||||
TP_PROTO(struct ieee802154_local *local, __le16 pan_id),
|
||||
TP_ARGS(local, pan_id),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(__le16, pan_id)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->pan_id = pan_id;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", pan id: 0x%04x", LOCAL_PR_ARG,
|
||||
le16_to_cpu(__entry->pan_id))
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_extended_addr,
|
||||
TP_PROTO(struct ieee802154_local *local, __le64 extended_addr),
|
||||
TP_ARGS(local, extended_addr),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(__le64, extended_addr)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->extended_addr = extended_addr;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", extended addr: 0x%llx", LOCAL_PR_ARG,
|
||||
le64_to_cpu(__entry->extended_addr))
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_pan_coord,
|
||||
TP_PROTO(struct ieee802154_local *local, bool is_coord),
|
||||
TP_ARGS(local, is_coord),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(bool, is_coord)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->is_coord = is_coord;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", is_coord: %s", LOCAL_PR_ARG,
|
||||
BOOL_TO_STR(__entry->is_coord))
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_csma_params,
|
||||
TP_PROTO(struct ieee802154_local *local, u8 min_be, u8 max_be,
|
||||
u8 max_csma_backoffs),
|
||||
TP_ARGS(local, min_be, max_be, max_csma_backoffs),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(u8, min_be)
|
||||
__field(u8, max_be)
|
||||
__field(u8, max_csma_backoffs)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN,
|
||||
__entry->min_be = min_be;
|
||||
__entry->max_be = max_be;
|
||||
__entry->max_csma_backoffs = max_csma_backoffs;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", min be: %d, max be: %d, max csma backoffs: %d",
|
||||
LOCAL_PR_ARG, __entry->min_be, __entry->max_be,
|
||||
__entry->max_csma_backoffs)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_max_frame_retries,
|
||||
TP_PROTO(struct ieee802154_local *local, s8 max_frame_retries),
|
||||
TP_ARGS(local, max_frame_retries),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(s8, max_frame_retries)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->max_frame_retries = max_frame_retries;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", max frame retries: %d", LOCAL_PR_ARG,
|
||||
__entry->max_frame_retries)
|
||||
);
|
||||
|
||||
TRACE_EVENT(802154_drv_set_promiscuous_mode,
|
||||
TP_PROTO(struct ieee802154_local *local, bool on),
|
||||
TP_ARGS(local, on),
|
||||
TP_STRUCT__entry(
|
||||
LOCAL_ENTRY
|
||||
__field(bool, on)
|
||||
),
|
||||
TP_fast_assign(
|
||||
LOCAL_ASSIGN;
|
||||
__entry->on = on;
|
||||
),
|
||||
TP_printk(LOCAL_PR_FMT ", promiscuous mode: %s", LOCAL_PR_ARG,
|
||||
BOOL_TO_STR(__entry->on))
|
||||
);
|
||||
|
||||
#endif /* !__MAC802154_DRIVER_TRACE || TRACE_HEADER_MULTI_READ */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE trace
|
||||
#include <trace/define_trace.h>
|
Loading…
Reference in New Issue
Block a user