Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next
Johan Hedberg say: ==================== pull request: bluetooth-next 2014-12-31 Here's the first batch of bluetooth patches for 3.20. - Cleanups & fixes to ieee802154 drivers - Fix synchronization of mgmt commands with respective HCI commands - Add self-tests for LE pairing crypto functionality - Remove 'BlueFritz!' specific handling from core using a new quirk flag - Public address configuration support for ath3012 - Refactor debugfs support into a dedicated file - Initial support for LE Data Length Extension feature from Bluetooth 4.2 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
6c032edc8a
|
@ -696,6 +696,8 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
|
|||
hdev->flush = bfusb_flush;
|
||||
hdev->send = bfusb_send_frame;
|
||||
|
||||
set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
|
||||
|
||||
if (hci_register_dev(hdev) < 0) {
|
||||
BT_ERR("Can't register HCI device");
|
||||
hci_free_dev(hdev);
|
||||
|
|
|
@ -49,6 +49,7 @@ static struct usb_driver btusb_driver;
|
|||
#define BTUSB_INTEL_BOOT 0x200
|
||||
#define BTUSB_BCM_PATCHRAM 0x400
|
||||
#define BTUSB_MARVELL 0x800
|
||||
#define BTUSB_AVM 0x1000
|
||||
|
||||
static const struct usb_device_id btusb_table[] = {
|
||||
/* Generic Bluetooth USB device */
|
||||
|
@ -85,7 +86,7 @@ static const struct usb_device_id btusb_table[] = {
|
|||
{ USB_DEVICE(0x05ac, 0x8281) },
|
||||
|
||||
/* AVM BlueFRITZ! USB v2.0 */
|
||||
{ USB_DEVICE(0x057c, 0x3800) },
|
||||
{ USB_DEVICE(0x057c, 0x3800), .driver_info = BTUSB_AVM },
|
||||
|
||||
/* Bluetooth Ultraport Module from IBM */
|
||||
{ USB_DEVICE(0x04bf, 0x030a) },
|
||||
|
@ -1943,6 +1944,31 @@ static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_set_bdaddr_ath3012(struct hci_dev *hdev,
|
||||
const bdaddr_t *bdaddr)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
u8 buf[10];
|
||||
long ret;
|
||||
|
||||
buf[0] = 0x01;
|
||||
buf[1] = 0x01;
|
||||
buf[2] = 0x00;
|
||||
buf[3] = sizeof(bdaddr_t);
|
||||
memcpy(buf + 4, bdaddr, sizeof(bdaddr_t));
|
||||
|
||||
skb = __hci_cmd_sync(hdev, 0xfc0b, sizeof(buf), buf, HCI_INIT_TIMEOUT);
|
||||
if (IS_ERR(skb)) {
|
||||
ret = PTR_ERR(skb);
|
||||
BT_ERR("%s: Change address command failed (%ld)",
|
||||
hdev->name, ret);
|
||||
return ret;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int btusb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
|
@ -2055,9 +2081,15 @@ static int btusb_probe(struct usb_interface *intf,
|
|||
if (id->driver_info & BTUSB_MARVELL)
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_marvell;
|
||||
|
||||
if (id->driver_info & BTUSB_AVM)
|
||||
set_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks);
|
||||
|
||||
if (id->driver_info & BTUSB_INTEL_BOOT)
|
||||
set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks);
|
||||
|
||||
if (id->driver_info & BTUSB_ATH3012)
|
||||
hdev->set_bdaddr = btusb_set_bdaddr_ath3012;
|
||||
|
||||
/* Interface numbers are hardcoded in the specification */
|
||||
data->isoc = usb_ifnum_to_if(data->udev, 1);
|
||||
|
||||
|
|
|
@ -450,7 +450,7 @@ at86rf230_async_error_recover(void *context)
|
|||
ieee802154_wake_queue(lp->hw);
|
||||
}
|
||||
|
||||
static void
|
||||
static inline void
|
||||
at86rf230_async_error(struct at86rf230_local *lp,
|
||||
struct at86rf230_state_change *ctx, int rc)
|
||||
{
|
||||
|
@ -524,7 +524,6 @@ at86rf230_async_state_assert(void *context)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
dev_warn(&lp->spi->dev, "unexcept state change from 0x%02x to 0x%02x. Actual state: 0x%02x\n",
|
||||
ctx->from_state, ctx->to_state, trx_state);
|
||||
}
|
||||
|
@ -655,7 +654,7 @@ at86rf230_async_state_change_start(void *context)
|
|||
if (ctx->irq_enable)
|
||||
enable_irq(lp->spi->irq);
|
||||
|
||||
at86rf230_async_error(lp, &lp->state, rc);
|
||||
at86rf230_async_error(lp, ctx, rc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,10 +714,7 @@ at86rf230_tx_complete(void *context)
|
|||
|
||||
enable_irq(lp->spi->irq);
|
||||
|
||||
if (lp->max_frame_retries <= 0)
|
||||
ieee802154_xmit_complete(lp->hw, skb, true);
|
||||
else
|
||||
ieee802154_xmit_complete(lp->hw, skb, false);
|
||||
ieee802154_xmit_complete(lp->hw, skb, !lp->tx_aret);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -753,16 +749,13 @@ at86rf230_tx_trac_check(void *context)
|
|||
* to STATE_FORCE_TRX_OFF then STATE_TX_ON to recover the transceiver
|
||||
* state to TX_ON.
|
||||
*/
|
||||
if (trac) {
|
||||
if (trac)
|
||||
at86rf230_async_state_change(lp, ctx, STATE_FORCE_TRX_OFF,
|
||||
at86rf230_tx_trac_error, true);
|
||||
return;
|
||||
}
|
||||
|
||||
at86rf230_tx_on(context);
|
||||
else
|
||||
at86rf230_tx_on(context);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
at86rf230_tx_trac_status(void *context)
|
||||
{
|
||||
|
@ -1082,7 +1075,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw,
|
|||
u16 addr = le16_to_cpu(filt->short_addr);
|
||||
|
||||
dev_vdbg(&lp->spi->dev,
|
||||
"at86rf230_set_hw_addr_filt called for saddr\n");
|
||||
"at86rf230_set_hw_addr_filt called for saddr\n");
|
||||
__at86rf230_write(lp, RG_SHORT_ADDR_0, addr);
|
||||
__at86rf230_write(lp, RG_SHORT_ADDR_1, addr >> 8);
|
||||
}
|
||||
|
@ -1091,7 +1084,7 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw,
|
|||
u16 pan = le16_to_cpu(filt->pan_id);
|
||||
|
||||
dev_vdbg(&lp->spi->dev,
|
||||
"at86rf230_set_hw_addr_filt called for pan id\n");
|
||||
"at86rf230_set_hw_addr_filt called for pan id\n");
|
||||
__at86rf230_write(lp, RG_PAN_ID_0, pan);
|
||||
__at86rf230_write(lp, RG_PAN_ID_1, pan >> 8);
|
||||
}
|
||||
|
@ -1101,14 +1094,14 @@ at86rf230_set_hw_addr_filt(struct ieee802154_hw *hw,
|
|||
|
||||
memcpy(addr, &filt->ieee_addr, 8);
|
||||
dev_vdbg(&lp->spi->dev,
|
||||
"at86rf230_set_hw_addr_filt called for IEEE addr\n");
|
||||
"at86rf230_set_hw_addr_filt called for IEEE addr\n");
|
||||
for (i = 0; i < 8; i++)
|
||||
__at86rf230_write(lp, RG_IEEE_ADDR_0 + i, addr[i]);
|
||||
}
|
||||
|
||||
if (changed & IEEE802154_AFILT_PANC_CHANGED) {
|
||||
dev_vdbg(&lp->spi->dev,
|
||||
"at86rf230_set_hw_addr_filt called for panc change\n");
|
||||
"at86rf230_set_hw_addr_filt called for panc change\n");
|
||||
if (filt->pan_coord)
|
||||
at86rf230_write_subreg(lp, SR_AACK_I_AM_COORD, 1);
|
||||
else
|
||||
|
@ -1146,11 +1139,37 @@ at86rf230_set_lbt(struct ieee802154_hw *hw, bool on)
|
|||
}
|
||||
|
||||
static int
|
||||
at86rf230_set_cca_mode(struct ieee802154_hw *hw, u8 mode)
|
||||
at86rf230_set_cca_mode(struct ieee802154_hw *hw,
|
||||
const struct wpan_phy_cca *cca)
|
||||
{
|
||||
struct at86rf230_local *lp = hw->priv;
|
||||
u8 val;
|
||||
|
||||
return at86rf230_write_subreg(lp, SR_CCA_MODE, mode);
|
||||
/* mapping 802.15.4 to driver spec */
|
||||
switch (cca->mode) {
|
||||
case NL802154_CCA_ENERGY:
|
||||
val = 1;
|
||||
break;
|
||||
case NL802154_CCA_CARRIER:
|
||||
val = 2;
|
||||
break;
|
||||
case NL802154_CCA_ENERGY_CARRIER:
|
||||
switch (cca->opt) {
|
||||
case NL802154_CCA_OPT_ENERGY_CARRIER_AND:
|
||||
val = 3;
|
||||
break;
|
||||
case NL802154_CCA_OPT_ENERGY_CARRIER_OR:
|
||||
val = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return at86rf230_write_subreg(lp, SR_CCA_MODE, val);
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -1400,7 +1419,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = __at86rf230_read(lp, RG_PART_NUM, &version);
|
||||
rc = __at86rf230_read(lp, RG_VERSION_NUM, &version);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -1410,11 +1429,12 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
lp->hw->extra_tx_headroom = 0;
|
||||
lp->hw->flags = IEEE802154_HW_TX_OMIT_CKSUM | IEEE802154_HW_AACK |
|
||||
IEEE802154_HW_TXPOWER | IEEE802154_HW_ARET |
|
||||
IEEE802154_HW_AFILT | IEEE802154_HW_PROMISCUOUS;
|
||||
|
||||
lp->hw->phy->cca.mode = NL802154_CCA_ENERGY;
|
||||
|
||||
switch (part) {
|
||||
case 2:
|
||||
chip = "at86rf230";
|
||||
|
@ -1429,16 +1449,12 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
break;
|
||||
case 7:
|
||||
chip = "at86rf212";
|
||||
if (version == 1) {
|
||||
lp->data = &at86rf212_data;
|
||||
lp->hw->flags |= IEEE802154_HW_LBT;
|
||||
lp->hw->phy->channels_supported[0] = 0x00007FF;
|
||||
lp->hw->phy->channels_supported[2] = 0x00007FF;
|
||||
lp->hw->phy->current_channel = 5;
|
||||
lp->hw->phy->symbol_duration = 25;
|
||||
} else {
|
||||
rc = -ENOTSUPP;
|
||||
}
|
||||
lp->data = &at86rf212_data;
|
||||
lp->hw->flags |= IEEE802154_HW_LBT;
|
||||
lp->hw->phy->channels_supported[0] = 0x00007FF;
|
||||
lp->hw->phy->channels_supported[2] = 0x00007FF;
|
||||
lp->hw->phy->current_channel = 5;
|
||||
lp->hw->phy->symbol_duration = 25;
|
||||
break;
|
||||
case 11:
|
||||
chip = "at86rf233";
|
||||
|
@ -1448,7 +1464,7 @@ at86rf230_detect_device(struct at86rf230_local *lp)
|
|||
lp->hw->phy->symbol_duration = 16;
|
||||
break;
|
||||
default:
|
||||
chip = "unkown";
|
||||
chip = "unknown";
|
||||
rc = -ENOTSUPP;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
#include <linux/workqueue.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/ieee802154.h>
|
||||
|
||||
|
@ -513,7 +512,6 @@ err_tx:
|
|||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int cc2520_rx(struct cc2520_private *priv)
|
||||
{
|
||||
u8 len = 0, lqi = 0, bytes = 1;
|
||||
|
@ -652,6 +650,7 @@ static int cc2520_register(struct cc2520_private *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->channels_supported[0] = 0x7FFF800;
|
||||
|
@ -842,24 +841,15 @@ done:
|
|||
static int cc2520_probe(struct spi_device *spi)
|
||||
{
|
||||
struct cc2520_private *priv;
|
||||
struct pinctrl *pinctrl;
|
||||
struct cc2520_platform_data *pdata;
|
||||
int ret;
|
||||
|
||||
priv = devm_kzalloc(&spi->dev,
|
||||
sizeof(struct cc2520_private), GFP_KERNEL);
|
||||
if (!priv) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ret;
|
||||
}
|
||||
priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
spi_set_drvdata(spi, priv);
|
||||
|
||||
pinctrl = devm_pinctrl_get_select_default(&spi->dev);
|
||||
if (IS_ERR(pinctrl))
|
||||
dev_warn(&spi->dev,
|
||||
"pinctrl pins are not configured\n");
|
||||
|
||||
pdata = cc2520_get_platform_data(spi);
|
||||
if (!pdata) {
|
||||
dev_err(&spi->dev, "no platform data\n");
|
||||
|
@ -870,10 +860,8 @@ static int cc2520_probe(struct spi_device *spi)
|
|||
|
||||
priv->buf = devm_kzalloc(&spi->dev,
|
||||
SPI_COMMAND_BUFFER, GFP_KERNEL);
|
||||
if (!priv->buf) {
|
||||
ret = -ENOMEM;
|
||||
goto err_ret;
|
||||
}
|
||||
if (!priv->buf)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&priv->buffer_mutex);
|
||||
INIT_WORK(&priv->fifop_irqwork, cc2520_fifop_irqwork);
|
||||
|
@ -947,7 +935,6 @@ static int cc2520_probe(struct spi_device *spi)
|
|||
if (ret)
|
||||
goto err_hw_init;
|
||||
|
||||
|
||||
gpio_set_value(pdata->vreg, HIGH);
|
||||
usleep_range(100, 150);
|
||||
|
||||
|
@ -991,8 +978,6 @@ static int cc2520_probe(struct spi_device *spi)
|
|||
err_hw_init:
|
||||
mutex_destroy(&priv->buffer_mutex);
|
||||
flush_work(&priv->fifop_irqwork);
|
||||
|
||||
err_ret:
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -289,7 +289,7 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
|
|||
goto out;
|
||||
|
||||
/* Range check the RX FIFO length, accounting for the one-byte
|
||||
* length field at the begining. */
|
||||
* length field at the beginning. */
|
||||
if (rx_len > RX_FIFO_SIZE-1) {
|
||||
dev_err(printdev(devrec), "Invalid length read from device. Performing short read.\n");
|
||||
rx_len = RX_FIFO_SIZE-1;
|
||||
|
@ -323,7 +323,7 @@ static int mrf24j40_read_rx_buf(struct mrf24j40 *devrec,
|
|||
|
||||
#ifdef DEBUG
|
||||
print_hex_dump(KERN_DEBUG, "mrf24j40 rx: ",
|
||||
DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
|
||||
DUMP_PREFIX_OFFSET, 16, 1, data, *len, 0);
|
||||
pr_debug("mrf24j40 rx: lqi: %02hhx rssi: %02hhx\n",
|
||||
lqi_rssi[0], lqi_rssi[1]);
|
||||
#endif
|
||||
|
@ -521,7 +521,7 @@ static int mrf24j40_filter(struct ieee802154_hw *hw,
|
|||
*/
|
||||
|
||||
dev_dbg(printdev(devrec), "Set Pan Coord to %s\n",
|
||||
filt->pan_coord ? "on" : "off");
|
||||
filt->pan_coord ? "on" : "off");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -102,6 +102,16 @@ enum {
|
|||
*/
|
||||
HCI_QUIRK_FIXUP_BUFFER_SIZE,
|
||||
|
||||
/* When this quirk is set, then the HCI Read Local Supported
|
||||
* Commands command is not supported. In general Bluetooth 1.2
|
||||
* and later controllers should support this command. However
|
||||
* some controllers indicate Bluetooth 1.2 support, but do
|
||||
* not support this command.
|
||||
*
|
||||
* This quirk must be set before hci_register_dev is called.
|
||||
*/
|
||||
HCI_QUIRK_BROKEN_LOCAL_COMMANDS,
|
||||
|
||||
/* When this quirk is set, then no stored link key handling
|
||||
* is performed. This is mainly due to the fact that the
|
||||
* HCI Delete Stored Link Key command is advertised, but
|
||||
|
@ -343,6 +353,7 @@ enum {
|
|||
#define HCI_LE_ENCRYPTION 0x01
|
||||
#define HCI_LE_CONN_PARAM_REQ_PROC 0x02
|
||||
#define HCI_LE_PING 0x10
|
||||
#define HCI_LE_DATA_LEN_EXT 0x20
|
||||
#define HCI_LE_EXT_SCAN_POLICY 0x80
|
||||
|
||||
/* Connection modes */
|
||||
|
@ -1371,6 +1382,39 @@ struct hci_cp_le_conn_param_req_neg_reply {
|
|||
__u8 reason;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_SET_DATA_LEN 0x2022
|
||||
struct hci_cp_le_set_data_len {
|
||||
__le16 handle;
|
||||
__le16 tx_len;
|
||||
__le16 tx_time;
|
||||
} __packed;
|
||||
struct hci_rp_le_set_data_len {
|
||||
__u8 status;
|
||||
__le16 handle;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_DEF_DATA_LEN 0x2023
|
||||
struct hci_rp_le_read_def_data_len {
|
||||
__u8 status;
|
||||
__le16 tx_len;
|
||||
__le16 tx_time;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_WRITE_DEF_DATA_LEN 0x2024
|
||||
struct hci_cp_le_write_def_data_len {
|
||||
__le16 tx_len;
|
||||
__le16 tx_time;
|
||||
} __packed;
|
||||
|
||||
#define HCI_OP_LE_READ_MAX_DATA_LEN 0x202f
|
||||
struct hci_rp_le_read_max_data_len {
|
||||
__u8 status;
|
||||
__le16 tx_len;
|
||||
__le16 tx_time;
|
||||
__le16 rx_len;
|
||||
__le16 rx_time;
|
||||
} __packed;
|
||||
|
||||
/* ---- HCI Events ---- */
|
||||
#define HCI_EV_INQUIRY_COMPLETE 0x01
|
||||
|
||||
|
@ -1796,6 +1840,15 @@ struct hci_ev_le_remote_conn_param_req {
|
|||
__le16 timeout;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_DATA_LEN_CHANGE 0x07
|
||||
struct hci_ev_le_data_len_change {
|
||||
__le16 handle;
|
||||
__le16 tx_len;
|
||||
__le16 tx_time;
|
||||
__le16 rx_len;
|
||||
__le16 rx_time;
|
||||
} __packed;
|
||||
|
||||
#define HCI_EV_LE_DIRECT_ADV_REPORT 0x0B
|
||||
struct hci_ev_le_direct_adv_info {
|
||||
__u8 evt_type;
|
||||
|
|
|
@ -220,6 +220,12 @@ struct hci_dev {
|
|||
__u16 le_conn_max_interval;
|
||||
__u16 le_conn_latency;
|
||||
__u16 le_supv_timeout;
|
||||
__u16 le_def_tx_len;
|
||||
__u16 le_def_tx_time;
|
||||
__u16 le_max_tx_len;
|
||||
__u16 le_max_tx_time;
|
||||
__u16 le_max_rx_len;
|
||||
__u16 le_max_rx_time;
|
||||
__u16 discov_interleaved_timeout;
|
||||
__u16 conn_info_min_age;
|
||||
__u16 conn_info_max_age;
|
||||
|
@ -434,6 +440,7 @@ struct hci_conn {
|
|||
struct delayed_work le_conn_timeout;
|
||||
|
||||
struct device dev;
|
||||
struct dentry *debugfs;
|
||||
|
||||
struct hci_dev *hdev;
|
||||
void *l2cap_data;
|
||||
|
@ -920,8 +927,6 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
|
|||
bdaddr_t *addr, u8 addr_type);
|
||||
struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
|
||||
bdaddr_t *addr, u8 addr_type);
|
||||
int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
|
||||
u8 auto_connect);
|
||||
void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type);
|
||||
void hci_conn_params_clear_all(struct hci_dev *hdev);
|
||||
void hci_conn_params_clear_disabled(struct hci_dev *hdev);
|
||||
|
@ -930,8 +935,6 @@ struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list,
|
|||
bdaddr_t *addr,
|
||||
u8 addr_type);
|
||||
|
||||
void hci_update_background_scan(struct hci_dev *hdev);
|
||||
|
||||
void hci_uuids_clear(struct hci_dev *hdev);
|
||||
|
||||
void hci_link_keys_clear(struct hci_dev *hdev);
|
||||
|
@ -1284,30 +1287,8 @@ static inline int hci_check_conn_params(u16 min, u16 max, u16 latency,
|
|||
int hci_register_cb(struct hci_cb *hcb);
|
||||
int hci_unregister_cb(struct hci_cb *hcb);
|
||||
|
||||
struct hci_request {
|
||||
struct hci_dev *hdev;
|
||||
struct sk_buff_head cmd_q;
|
||||
|
||||
/* If something goes wrong when building the HCI request, the error
|
||||
* value is stored in this field.
|
||||
*/
|
||||
int err;
|
||||
};
|
||||
|
||||
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
||||
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
||||
const void *param);
|
||||
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||
const void *param, u8 event);
|
||||
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
|
||||
bool hci_req_pending(struct hci_dev *hdev);
|
||||
|
||||
void hci_req_add_le_scan_disable(struct hci_request *req);
|
||||
void hci_req_add_le_passive_scan(struct hci_request *req);
|
||||
|
||||
void hci_update_page_scan(struct hci_dev *hdev, struct hci_request *req);
|
||||
|
||||
struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
const void *param, u32 timeout);
|
||||
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
|
@ -1417,8 +1398,6 @@ 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]);
|
||||
|
||||
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
||||
u8 *own_addr_type);
|
||||
void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
|
||||
u8 *bdaddr_type);
|
||||
|
||||
|
|
|
@ -248,6 +248,7 @@ struct l2cap_conn_rsp {
|
|||
#define L2CAP_PSM_SDP 0x0001
|
||||
#define L2CAP_PSM_RFCOMM 0x0003
|
||||
#define L2CAP_PSM_3DSP 0x0021
|
||||
#define L2CAP_PSM_IPSP 0x0023 /* 6LoWPAN */
|
||||
|
||||
/* channel identifier */
|
||||
#define L2CAP_CID_SIGNALING 0x0001
|
||||
|
|
|
@ -24,8 +24,6 @@
|
|||
#ifndef __RFCOMM_H
|
||||
#define __RFCOMM_H
|
||||
|
||||
#define RFCOMM_PSM 3
|
||||
|
||||
#define RFCOMM_CONN_TIMEOUT (HZ * 30)
|
||||
#define RFCOMM_DISC_TIMEOUT (HZ * 20)
|
||||
#define RFCOMM_AUTH_TIMEOUT (HZ * 25)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <net/nl802154.h>
|
||||
|
||||
struct wpan_phy;
|
||||
struct wpan_phy_cca;
|
||||
|
||||
struct cfg802154_ops {
|
||||
struct net_device * (*add_virtual_intf_deprecated)(struct wpan_phy *wpan_phy,
|
||||
|
@ -39,6 +40,8 @@ struct cfg802154_ops {
|
|||
int (*del_virtual_intf)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev);
|
||||
int (*set_channel)(struct wpan_phy *wpan_phy, u8 page, u8 channel);
|
||||
int (*set_cca_mode)(struct wpan_phy *wpan_phy,
|
||||
const struct wpan_phy_cca *cca);
|
||||
int (*set_pan_id)(struct wpan_phy *wpan_phy,
|
||||
struct wpan_dev *wpan_dev, __le16 pan_id);
|
||||
int (*set_short_addr)(struct wpan_phy *wpan_phy,
|
||||
|
@ -56,6 +59,11 @@ struct cfg802154_ops {
|
|||
struct wpan_dev *wpan_dev, bool mode);
|
||||
};
|
||||
|
||||
struct wpan_phy_cca {
|
||||
enum nl802154_cca_modes mode;
|
||||
enum nl802154_cca_opts opt;
|
||||
};
|
||||
|
||||
struct wpan_phy {
|
||||
struct mutex pib_lock;
|
||||
|
||||
|
@ -76,7 +84,7 @@ struct wpan_phy {
|
|||
u8 current_page;
|
||||
u32 channels_supported[IEEE802154_MAX_PAGE + 1];
|
||||
s8 transmit_power;
|
||||
u8 cca_mode;
|
||||
struct wpan_phy_cca cca;
|
||||
|
||||
__le64 perm_extended_addr;
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#include <linux/skbuff.h>
|
||||
#include <linux/ieee802154.h>
|
||||
|
||||
#include <net/cfg802154.h>
|
||||
|
||||
struct ieee802154_sechdr {
|
||||
#if defined(__LITTLE_ENDIAN_BITFIELD)
|
||||
u8 level:3,
|
||||
|
@ -337,7 +339,7 @@ struct ieee802154_mac_params {
|
|||
s8 frame_retries;
|
||||
|
||||
bool lbt;
|
||||
u8 cca_mode;
|
||||
struct wpan_phy_cca cca;
|
||||
s32 cca_ed_level;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <linux/ieee802154.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include <net/cfg802154.h>
|
||||
|
||||
/* General MAC frame format:
|
||||
* 2 bytes: Frame Control
|
||||
* 1 byte: Sequence Number
|
||||
|
@ -212,7 +214,8 @@ struct ieee802154_ops {
|
|||
unsigned long changed);
|
||||
int (*set_txpower)(struct ieee802154_hw *hw, int db);
|
||||
int (*set_lbt)(struct ieee802154_hw *hw, bool on);
|
||||
int (*set_cca_mode)(struct ieee802154_hw *hw, u8 mode);
|
||||
int (*set_cca_mode)(struct ieee802154_hw *hw,
|
||||
const struct wpan_phy_cca *cca);
|
||||
int (*set_cca_ed_level)(struct ieee802154_hw *hw,
|
||||
s32 level);
|
||||
int (*set_csma_params)(struct ieee802154_hw *hw,
|
||||
|
|
|
@ -82,7 +82,7 @@ enum nl802154_attrs {
|
|||
NL802154_ATTR_TX_POWER,
|
||||
|
||||
NL802154_ATTR_CCA_MODE,
|
||||
NL802154_ATTR_CCA_MODE3_AND,
|
||||
NL802154_ATTR_CCA_OPT,
|
||||
NL802154_ATTR_CCA_ED_LEVEL,
|
||||
|
||||
NL802154_ATTR_MAX_FRAME_RETRIES,
|
||||
|
@ -119,4 +119,47 @@ enum nl802154_iftype {
|
|||
NL802154_IFTYPE_MAX = NUM_NL802154_IFTYPES - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl802154_cca_modes - cca modes
|
||||
*
|
||||
* @__NL802154_CCA_INVALID: cca mode number 0 is reserved
|
||||
* @NL802154_CCA_ENERGY: Energy above threshold
|
||||
* @NL802154_CCA_CARRIER: Carrier sense only
|
||||
* @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
|
||||
* the multiplexed preamble
|
||||
* @__NL802154_CCA_ATTR_AFTER_LAST: Internal
|
||||
* @NL802154_CCA_ATTR_MAX: Maximum CCA attribute number
|
||||
*/
|
||||
enum nl802154_cca_modes {
|
||||
__NL802154_CCA_INVALID,
|
||||
NL802154_CCA_ENERGY,
|
||||
NL802154_CCA_CARRIER,
|
||||
NL802154_CCA_ENERGY_CARRIER,
|
||||
NL802154_CCA_ALOHA,
|
||||
NL802154_CCA_UWB_SHR,
|
||||
NL802154_CCA_UWB_MULTIPEXED,
|
||||
|
||||
/* keep last */
|
||||
__NL802154_CCA_ATTR_AFTER_LAST,
|
||||
NL802154_CCA_ATTR_MAX = __NL802154_CCA_ATTR_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
/**
|
||||
* enum nl802154_cca_opts - additional options for cca modes
|
||||
*
|
||||
* @NL802154_CCA_OPT_ENERGY_CARRIER_OR: NL802154_CCA_ENERGY_CARRIER with OR
|
||||
* @NL802154_CCA_OPT_ENERGY_CARRIER_AND: NL802154_CCA_ENERGY_CARRIER with AND
|
||||
*/
|
||||
enum nl802154_cca_opts {
|
||||
NL802154_CCA_OPT_ENERGY_CARRIER_AND,
|
||||
NL802154_CCA_OPT_ENERGY_CARRIER_OR,
|
||||
|
||||
/* keep last */
|
||||
__NL802154_CCA_OPT_ATTR_AFTER_LAST,
|
||||
NL802154_CCA_OPT_ATTR_MAX = __NL802154_CCA_OPT_ATTR_AFTER_LAST - 1
|
||||
};
|
||||
|
||||
#endif /* __NL802154_H */
|
||||
|
|
|
@ -64,4 +64,31 @@ config BT_6LOWPAN
|
|||
help
|
||||
IPv6 compression over Bluetooth Low Energy.
|
||||
|
||||
config BT_SELFTEST
|
||||
bool "Bluetooth self testing support"
|
||||
depends on BT && DEBUG_KERNEL
|
||||
help
|
||||
Run self tests when initializing the Bluetooth subsystem. This
|
||||
is a developer option and can cause significant delay when booting
|
||||
the system.
|
||||
|
||||
When the Bluetooth subsystem is built as module, then the test
|
||||
cases are run first thing at module load time. When the Bluetooth
|
||||
subsystem is compiled into the kernel image, then the test cases
|
||||
are run late in the initcall hierarchy.
|
||||
|
||||
config BT_SELFTEST_ECDH
|
||||
bool "ECDH test cases"
|
||||
depends on BT_LE && BT_SELFTEST
|
||||
help
|
||||
Run test cases for ECDH cryptographic functionality used by the
|
||||
Bluetooth Low Energy Secure Connections feature.
|
||||
|
||||
config BT_SELFTEST_SMP
|
||||
bool "SMP test cases"
|
||||
depends on BT_LE && BT_SELFTEST
|
||||
help
|
||||
Run test cases for SMP cryptographic functionality, including both
|
||||
legacy SMP as well as the Secure Connections features.
|
||||
|
||||
source "drivers/bluetooth/Kconfig"
|
||||
|
|
|
@ -13,6 +13,8 @@ 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 \
|
||||
a2mp.o amp.o ecc.o
|
||||
a2mp.o amp.o ecc.o hci_request.o hci_debugfs.o
|
||||
|
||||
bluetooth-$(CONFIG_BT_SELFTEST) += selftest.o
|
||||
|
||||
subdir-ccflags-y += -D__CHECK_ENDIAN__
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <linux/proc_fs.h>
|
||||
|
||||
#include "selftest.h"
|
||||
|
||||
#define VERSION "2.20"
|
||||
|
||||
/* Bluetooth sockets */
|
||||
|
@ -716,6 +718,10 @@ static int __init bt_init(void)
|
|||
|
||||
BT_INFO("Core ver %s", VERSION);
|
||||
|
||||
err = bt_selftest();
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
bt_debugfs = debugfs_create_dir("bluetooth", NULL);
|
||||
|
||||
err = bt_sysfs_init();
|
||||
|
|
|
@ -25,11 +25,13 @@
|
|||
/* Bluetooth HCI connection handling. */
|
||||
|
||||
#include <linux/export.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/l2cap.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "smp.h"
|
||||
#include "a2mp.h"
|
||||
|
||||
|
@ -546,6 +548,8 @@ int hci_conn_del(struct hci_conn *conn)
|
|||
|
||||
hci_conn_del_sysfs(conn);
|
||||
|
||||
debugfs_remove_recursive(conn->debugfs);
|
||||
|
||||
if (test_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags))
|
||||
hci_conn_params_del(conn->hdev, &conn->dst, conn->dst_type);
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
1076
net/bluetooth/hci_debugfs.c
Normal file
1076
net/bluetooth/hci_debugfs.c
Normal file
File diff suppressed because it is too large
Load diff
26
net/bluetooth/hci_debugfs.h
Normal file
26
net/bluetooth/hci_debugfs.h
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
void hci_debugfs_create_common(struct hci_dev *hdev);
|
||||
void hci_debugfs_create_bredr(struct hci_dev *hdev);
|
||||
void hci_debugfs_create_le(struct hci_dev *hdev);
|
||||
void hci_debugfs_create_conn(struct hci_conn *conn);
|
|
@ -30,6 +30,8 @@
|
|||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/mgmt.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "hci_debugfs.h"
|
||||
#include "a2mp.h"
|
||||
#include "amp.h"
|
||||
#include "smp.h"
|
||||
|
@ -1282,6 +1284,55 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev,
|
|||
memcpy(hdev->le_states, rp->le_states, 8);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_def_data_len(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_def_data_len *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hdev->le_def_tx_len = le16_to_cpu(rp->tx_len);
|
||||
hdev->le_def_tx_time = le16_to_cpu(rp->tx_time);
|
||||
}
|
||||
|
||||
static void hci_cc_le_write_def_data_len(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_cp_le_write_def_data_len *sent;
|
||||
__u8 status = *((__u8 *) skb->data);
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, status);
|
||||
|
||||
if (status)
|
||||
return;
|
||||
|
||||
sent = hci_sent_cmd_data(hdev, HCI_OP_LE_WRITE_DEF_DATA_LEN);
|
||||
if (!sent)
|
||||
return;
|
||||
|
||||
hdev->le_def_tx_len = le16_to_cpu(sent->tx_len);
|
||||
hdev->le_def_tx_time = le16_to_cpu(sent->tx_time);
|
||||
}
|
||||
|
||||
static void hci_cc_le_read_max_data_len(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct hci_rp_le_read_max_data_len *rp = (void *) skb->data;
|
||||
|
||||
BT_DBG("%s status 0x%2.2x", hdev->name, rp->status);
|
||||
|
||||
if (rp->status)
|
||||
return;
|
||||
|
||||
hdev->le_max_tx_len = le16_to_cpu(rp->tx_len);
|
||||
hdev->le_max_tx_time = le16_to_cpu(rp->tx_time);
|
||||
hdev->le_max_rx_len = le16_to_cpu(rp->rx_len);
|
||||
hdev->le_max_rx_time = le16_to_cpu(rp->rx_time);
|
||||
}
|
||||
|
||||
static void hci_cc_write_le_host_supported(struct hci_dev *hdev,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
@ -2115,6 +2166,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
} else
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
if (test_bit(HCI_AUTH, &hdev->flags))
|
||||
|
@ -2130,7 +2182,7 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_send_cmd(hdev, HCI_OP_READ_REMOTE_FEATURES,
|
||||
sizeof(cp), &cp);
|
||||
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
hci_update_page_scan(hdev);
|
||||
}
|
||||
|
||||
/* Set packet type for incoming connection */
|
||||
|
@ -2316,7 +2368,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
if (test_bit(HCI_CONN_FLUSH_KEY, &conn->flags))
|
||||
hci_remove_link_key(hdev, &conn->dst);
|
||||
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
hci_update_page_scan(hdev);
|
||||
}
|
||||
|
||||
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||
|
@ -2854,6 +2906,18 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
hci_cc_le_read_supported_states(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_DEF_DATA_LEN:
|
||||
hci_cc_le_read_def_data_len(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_WRITE_DEF_DATA_LEN:
|
||||
hci_cc_le_write_def_data_len(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_LE_READ_MAX_DATA_LEN:
|
||||
hci_cc_le_read_max_data_len(hdev, skb);
|
||||
break;
|
||||
|
||||
case HCI_OP_WRITE_LE_HOST_SUPPORTED:
|
||||
hci_cc_write_le_host_supported(hdev, skb);
|
||||
break;
|
||||
|
@ -3584,6 +3648,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev,
|
|||
conn->handle = __le16_to_cpu(ev->handle);
|
||||
conn->state = BT_CONNECTED;
|
||||
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
break;
|
||||
|
||||
|
@ -4124,6 +4189,7 @@ static void hci_phy_link_complete_evt(struct hci_dev *hdev,
|
|||
hcon->disc_timeout = HCI_DISCONN_TIMEOUT;
|
||||
hci_conn_drop(hcon);
|
||||
|
||||
hci_debugfs_create_conn(hcon);
|
||||
hci_conn_add_sysfs(hcon);
|
||||
|
||||
amp_physical_cfm(bredr_hcon, hcon);
|
||||
|
@ -4330,6 +4396,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb)
|
|||
conn->le_conn_latency = le16_to_cpu(ev->latency);
|
||||
conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout);
|
||||
|
||||
hci_debugfs_create_conn(conn);
|
||||
hci_conn_add_sysfs(conn);
|
||||
|
||||
hci_proto_connect_cfm(conn, ev->status);
|
||||
|
|
555
net/bluetooth/hci_request.c
Normal file
555
net/bluetooth/hci_request.c
Normal file
|
@ -0,0 +1,555 @@
|
|||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "smp.h"
|
||||
#include "hci_request.h"
|
||||
|
||||
void hci_req_init(struct hci_request *req, struct hci_dev *hdev)
|
||||
{
|
||||
skb_queue_head_init(&req->cmd_q);
|
||||
req->hdev = hdev;
|
||||
req->err = 0;
|
||||
}
|
||||
|
||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags;
|
||||
|
||||
BT_DBG("length %u", skb_queue_len(&req->cmd_q));
|
||||
|
||||
/* If an error occurred during request building, remove all HCI
|
||||
* commands queued on the HCI request queue.
|
||||
*/
|
||||
if (req->err) {
|
||||
skb_queue_purge(&req->cmd_q);
|
||||
return req->err;
|
||||
}
|
||||
|
||||
/* Do not allow empty requests */
|
||||
if (skb_queue_empty(&req->cmd_q))
|
||||
return -ENODATA;
|
||||
|
||||
skb = skb_peek_tail(&req->cmd_q);
|
||||
bt_cb(skb)->req.complete = complete;
|
||||
|
||||
spin_lock_irqsave(&hdev->cmd_q.lock, flags);
|
||||
skb_queue_splice_tail(&req->cmd_q, &hdev->cmd_q);
|
||||
spin_unlock_irqrestore(&hdev->cmd_q.lock, flags);
|
||||
|
||||
queue_work(hdev->workqueue, &hdev->cmd_work);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
const void *param)
|
||||
{
|
||||
int len = HCI_COMMAND_HDR_SIZE + plen;
|
||||
struct hci_command_hdr *hdr;
|
||||
struct sk_buff *skb;
|
||||
|
||||
skb = bt_skb_alloc(len, GFP_ATOMIC);
|
||||
if (!skb)
|
||||
return NULL;
|
||||
|
||||
hdr = (struct hci_command_hdr *) skb_put(skb, HCI_COMMAND_HDR_SIZE);
|
||||
hdr->opcode = cpu_to_le16(opcode);
|
||||
hdr->plen = plen;
|
||||
|
||||
if (plen)
|
||||
memcpy(skb_put(skb, plen), param, plen);
|
||||
|
||||
BT_DBG("skb len %d", skb->len);
|
||||
|
||||
bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
|
||||
bt_cb(skb)->opcode = opcode;
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
/* Queue a command to an asynchronous HCI request */
|
||||
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||
const void *param, u8 event)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct sk_buff *skb;
|
||||
|
||||
BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
|
||||
|
||||
/* If an error occurred during request building, there is no point in
|
||||
* queueing the HCI command. We can simply return.
|
||||
*/
|
||||
if (req->err)
|
||||
return;
|
||||
|
||||
skb = hci_prepare_cmd(hdev, opcode, plen, param);
|
||||
if (!skb) {
|
||||
BT_ERR("%s no memory for command (opcode 0x%4.4x)",
|
||||
hdev->name, opcode);
|
||||
req->err = -ENOMEM;
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb_queue_empty(&req->cmd_q))
|
||||
bt_cb(skb)->req.start = true;
|
||||
|
||||
bt_cb(skb)->req.event = event;
|
||||
|
||||
skb_queue_tail(&req->cmd_q, skb);
|
||||
}
|
||||
|
||||
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
||||
const void *param)
|
||||
{
|
||||
hci_req_add_ev(req, opcode, plen, param, 0);
|
||||
}
|
||||
|
||||
void hci_req_add_le_scan_disable(struct hci_request *req)
|
||||
{
|
||||
struct hci_cp_le_set_scan_enable cp;
|
||||
|
||||
memset(&cp, 0, sizeof(cp));
|
||||
cp.enable = LE_SCAN_DISABLE;
|
||||
hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static void add_to_white_list(struct hci_request *req,
|
||||
struct hci_conn_params *params)
|
||||
{
|
||||
struct hci_cp_le_add_to_white_list cp;
|
||||
|
||||
cp.bdaddr_type = params->addr_type;
|
||||
bacpy(&cp.bdaddr, ¶ms->addr);
|
||||
|
||||
hci_req_add(req, HCI_OP_LE_ADD_TO_WHITE_LIST, sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
static u8 update_white_list(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_conn_params *params;
|
||||
struct bdaddr_list *b;
|
||||
uint8_t white_list_entries = 0;
|
||||
|
||||
/* Go through the current white list programmed into the
|
||||
* controller one by one and check if that address is still
|
||||
* in the list of pending connections or list of devices to
|
||||
* report. If not present in either list, then queue the
|
||||
* command to remove it from the controller.
|
||||
*/
|
||||
list_for_each_entry(b, &hdev->le_white_list, list) {
|
||||
struct hci_cp_le_del_from_white_list cp;
|
||||
|
||||
if (hci_pend_le_action_lookup(&hdev->pend_le_conns,
|
||||
&b->bdaddr, b->bdaddr_type) ||
|
||||
hci_pend_le_action_lookup(&hdev->pend_le_reports,
|
||||
&b->bdaddr, b->bdaddr_type)) {
|
||||
white_list_entries++;
|
||||
continue;
|
||||
}
|
||||
|
||||
cp.bdaddr_type = b->bdaddr_type;
|
||||
bacpy(&cp.bdaddr, &b->bdaddr);
|
||||
|
||||
hci_req_add(req, HCI_OP_LE_DEL_FROM_WHITE_LIST,
|
||||
sizeof(cp), &cp);
|
||||
}
|
||||
|
||||
/* Since all no longer valid white list entries have been
|
||||
* removed, walk through the list of pending connections
|
||||
* and ensure that any new device gets programmed into
|
||||
* the controller.
|
||||
*
|
||||
* If the list of the devices is larger than the list of
|
||||
* available white list entries in the controller, then
|
||||
* just abort and return filer policy value to not use the
|
||||
* white list.
|
||||
*/
|
||||
list_for_each_entry(params, &hdev->pend_le_conns, action) {
|
||||
if (hci_bdaddr_list_lookup(&hdev->le_white_list,
|
||||
¶ms->addr, params->addr_type))
|
||||
continue;
|
||||
|
||||
if (white_list_entries >= hdev->le_white_list_size) {
|
||||
/* Select filter policy to accept all advertising */
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
if (hci_find_irk_by_addr(hdev, ¶ms->addr,
|
||||
params->addr_type)) {
|
||||
/* White list can not be used with RPAs */
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
white_list_entries++;
|
||||
add_to_white_list(req, params);
|
||||
}
|
||||
|
||||
/* After adding all new pending connections, walk through
|
||||
* the list of pending reports and also add these to the
|
||||
* white list if there is still space.
|
||||
*/
|
||||
list_for_each_entry(params, &hdev->pend_le_reports, action) {
|
||||
if (hci_bdaddr_list_lookup(&hdev->le_white_list,
|
||||
¶ms->addr, params->addr_type))
|
||||
continue;
|
||||
|
||||
if (white_list_entries >= hdev->le_white_list_size) {
|
||||
/* Select filter policy to accept all advertising */
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
if (hci_find_irk_by_addr(hdev, ¶ms->addr,
|
||||
params->addr_type)) {
|
||||
/* White list can not be used with RPAs */
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
white_list_entries++;
|
||||
add_to_white_list(req, params);
|
||||
}
|
||||
|
||||
/* Select filter policy to use white list */
|
||||
return 0x01;
|
||||
}
|
||||
|
||||
void hci_req_add_le_passive_scan(struct hci_request *req)
|
||||
{
|
||||
struct hci_cp_le_set_scan_param param_cp;
|
||||
struct hci_cp_le_set_scan_enable enable_cp;
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 own_addr_type;
|
||||
u8 filter_policy;
|
||||
|
||||
/* Set require_privacy to false since no SCAN_REQ are send
|
||||
* during passive scanning. Not using an non-resolvable address
|
||||
* here is important so that peer devices using direct
|
||||
* advertising with our address will be correctly reported
|
||||
* by the controller.
|
||||
*/
|
||||
if (hci_update_random_address(req, false, &own_addr_type))
|
||||
return;
|
||||
|
||||
/* Adding or removing entries from the white list must
|
||||
* happen before enabling scanning. The controller does
|
||||
* not allow white list modification while scanning.
|
||||
*/
|
||||
filter_policy = update_white_list(req);
|
||||
|
||||
/* When the controller is using random resolvable addresses and
|
||||
* with that having LE privacy enabled, then controllers with
|
||||
* Extended Scanner Filter Policies support can now enable support
|
||||
* for handling directed advertising.
|
||||
*
|
||||
* So instead of using filter polices 0x00 (no whitelist)
|
||||
* and 0x01 (whitelist enabled) use the new filter policies
|
||||
* 0x02 (no whitelist) and 0x03 (whitelist enabled).
|
||||
*/
|
||||
if (test_bit(HCI_PRIVACY, &hdev->dev_flags) &&
|
||||
(hdev->le_features[0] & HCI_LE_EXT_SCAN_POLICY))
|
||||
filter_policy |= 0x02;
|
||||
|
||||
memset(¶m_cp, 0, sizeof(param_cp));
|
||||
param_cp.type = LE_SCAN_PASSIVE;
|
||||
param_cp.interval = cpu_to_le16(hdev->le_scan_interval);
|
||||
param_cp.window = cpu_to_le16(hdev->le_scan_window);
|
||||
param_cp.own_address_type = own_addr_type;
|
||||
param_cp.filter_policy = filter_policy;
|
||||
hci_req_add(req, HCI_OP_LE_SET_SCAN_PARAM, sizeof(param_cp),
|
||||
¶m_cp);
|
||||
|
||||
memset(&enable_cp, 0, sizeof(enable_cp));
|
||||
enable_cp.enable = LE_SCAN_ENABLE;
|
||||
enable_cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE;
|
||||
hci_req_add(req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(enable_cp),
|
||||
&enable_cp);
|
||||
}
|
||||
|
||||
static void set_random_addr(struct hci_request *req, bdaddr_t *rpa)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
|
||||
/* If we're advertising or initiating an LE connection we can't
|
||||
* go ahead and change the random address at this time. This is
|
||||
* because the eventual initiator address used for the
|
||||
* subsequently created connection will be undefined (some
|
||||
* controllers use the new address and others the one we had
|
||||
* when the operation started).
|
||||
*
|
||||
* In this kind of scenario skip the update and let the random
|
||||
* address be updated at the next cycle.
|
||||
*/
|
||||
if (test_bit(HCI_LE_ADV, &hdev->dev_flags) ||
|
||||
hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) {
|
||||
BT_DBG("Deferring random address update");
|
||||
set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags);
|
||||
return;
|
||||
}
|
||||
|
||||
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6, rpa);
|
||||
}
|
||||
|
||||
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
||||
u8 *own_addr_type)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
int err;
|
||||
|
||||
/* If privacy is enabled use a resolvable private address. If
|
||||
* current RPA has expired or there is something else than
|
||||
* the current RPA in use, then generate a new one.
|
||||
*/
|
||||
if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) {
|
||||
int to;
|
||||
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
|
||||
if (!test_and_clear_bit(HCI_RPA_EXPIRED, &hdev->dev_flags) &&
|
||||
!bacmp(&hdev->random_addr, &hdev->rpa))
|
||||
return 0;
|
||||
|
||||
err = smp_generate_rpa(hdev, hdev->irk, &hdev->rpa);
|
||||
if (err < 0) {
|
||||
BT_ERR("%s failed to generate new RPA", hdev->name);
|
||||
return err;
|
||||
}
|
||||
|
||||
set_random_addr(req, &hdev->rpa);
|
||||
|
||||
to = msecs_to_jiffies(hdev->rpa_timeout * 1000);
|
||||
queue_delayed_work(hdev->workqueue, &hdev->rpa_expired, to);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* In case of required privacy without resolvable private address,
|
||||
* use an non-resolvable private address. This is useful for active
|
||||
* scanning and non-connectable advertising.
|
||||
*/
|
||||
if (require_privacy) {
|
||||
bdaddr_t nrpa;
|
||||
|
||||
while (true) {
|
||||
/* The non-resolvable private address is generated
|
||||
* from random six bytes with the two most significant
|
||||
* bits cleared.
|
||||
*/
|
||||
get_random_bytes(&nrpa, 6);
|
||||
nrpa.b[5] &= 0x3f;
|
||||
|
||||
/* The non-resolvable private address shall not be
|
||||
* equal to the public address.
|
||||
*/
|
||||
if (bacmp(&hdev->bdaddr, &nrpa))
|
||||
break;
|
||||
}
|
||||
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
set_random_addr(req, &nrpa);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If forcing static address is in use or there is no public
|
||||
* address use the static address as random address (but skip
|
||||
* the HCI command if the current random address is already the
|
||||
* static one.
|
||||
*
|
||||
* In case BR/EDR has been disabled on a dual-mode controller
|
||||
* and a static address has been configured, then use that
|
||||
* address instead of the public BR/EDR address.
|
||||
*/
|
||||
if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
|
||||
!bacmp(&hdev->bdaddr, BDADDR_ANY) ||
|
||||
(!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
|
||||
bacmp(&hdev->static_addr, BDADDR_ANY))) {
|
||||
*own_addr_type = ADDR_LE_DEV_RANDOM;
|
||||
if (bacmp(&hdev->static_addr, &hdev->random_addr))
|
||||
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
|
||||
&hdev->static_addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Neither privacy nor static address is being used so use a
|
||||
* public address.
|
||||
*/
|
||||
*own_addr_type = ADDR_LE_DEV_PUBLIC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool disconnected_whitelist_entries(struct hci_dev *hdev)
|
||||
{
|
||||
struct bdaddr_list *b;
|
||||
|
||||
list_for_each_entry(b, &hdev->whitelist, list) {
|
||||
struct hci_conn *conn;
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &b->bdaddr);
|
||||
if (!conn)
|
||||
return true;
|
||||
|
||||
if (conn->state != BT_CONNECTED && conn->state != BT_CONFIG)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void __hci_update_page_scan(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
u8 scan;
|
||||
|
||||
if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags))
|
||||
return;
|
||||
|
||||
if (!hdev_is_powered(hdev))
|
||||
return;
|
||||
|
||||
if (mgmt_powering_down(hdev))
|
||||
return;
|
||||
|
||||
if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) ||
|
||||
disconnected_whitelist_entries(hdev))
|
||||
scan = SCAN_PAGE;
|
||||
else
|
||||
scan = SCAN_DISABLED;
|
||||
|
||||
if (test_bit(HCI_PSCAN, &hdev->flags) == !!(scan & SCAN_PAGE))
|
||||
return;
|
||||
|
||||
if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags))
|
||||
scan |= SCAN_INQUIRY;
|
||||
|
||||
hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
|
||||
}
|
||||
|
||||
void hci_update_page_scan(struct hci_dev *hdev)
|
||||
{
|
||||
struct hci_request req;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
__hci_update_page_scan(&req);
|
||||
hci_req_run(&req, NULL);
|
||||
}
|
||||
|
||||
/* This function controls the background scanning based on hdev->pend_le_conns
|
||||
* list. If there are pending LE connection we start the background scanning,
|
||||
* otherwise we stop it.
|
||||
*
|
||||
* This function requires the caller holds hdev->lock.
|
||||
*/
|
||||
void __hci_update_background_scan(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_conn *conn;
|
||||
|
||||
if (!test_bit(HCI_UP, &hdev->flags) ||
|
||||
test_bit(HCI_INIT, &hdev->flags) ||
|
||||
test_bit(HCI_SETUP, &hdev->dev_flags) ||
|
||||
test_bit(HCI_CONFIG, &hdev->dev_flags) ||
|
||||
test_bit(HCI_AUTO_OFF, &hdev->dev_flags) ||
|
||||
test_bit(HCI_UNREGISTER, &hdev->dev_flags))
|
||||
return;
|
||||
|
||||
/* No point in doing scanning if LE support hasn't been enabled */
|
||||
if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags))
|
||||
return;
|
||||
|
||||
/* If discovery is active don't interfere with it */
|
||||
if (hdev->discovery.state != DISCOVERY_STOPPED)
|
||||
return;
|
||||
|
||||
/* Reset RSSI and UUID filters when starting background scanning
|
||||
* since these filters are meant for service discovery only.
|
||||
*
|
||||
* The Start Discovery and Start Service Discovery operations
|
||||
* ensure to set proper values for RSSI threshold and UUID
|
||||
* filter list. So it is safe to just reset them here.
|
||||
*/
|
||||
hci_discovery_filter_clear(hdev);
|
||||
|
||||
if (list_empty(&hdev->pend_le_conns) &&
|
||||
list_empty(&hdev->pend_le_reports)) {
|
||||
/* If there is no pending LE connections or devices
|
||||
* to be scanned for, we should stop the background
|
||||
* scanning.
|
||||
*/
|
||||
|
||||
/* If controller is not scanning we are done. */
|
||||
if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags))
|
||||
return;
|
||||
|
||||
hci_req_add_le_scan_disable(req);
|
||||
|
||||
BT_DBG("%s stopping background scanning", hdev->name);
|
||||
} else {
|
||||
/* If there is at least one pending LE connection, we should
|
||||
* keep the background scan running.
|
||||
*/
|
||||
|
||||
/* If controller is connecting, we should not start scanning
|
||||
* since some controllers are not able to scan and connect at
|
||||
* the same time.
|
||||
*/
|
||||
conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT);
|
||||
if (conn)
|
||||
return;
|
||||
|
||||
/* If controller is currently scanning, we stop it to ensure we
|
||||
* don't miss any advertising (due to duplicates filter).
|
||||
*/
|
||||
if (test_bit(HCI_LE_SCAN, &hdev->dev_flags))
|
||||
hci_req_add_le_scan_disable(req);
|
||||
|
||||
hci_req_add_le_passive_scan(req);
|
||||
|
||||
BT_DBG("%s starting background scanning", hdev->name);
|
||||
}
|
||||
}
|
||||
|
||||
static void update_background_scan_complete(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
if (status)
|
||||
BT_DBG("HCI request failed to update background scanning: "
|
||||
"status 0x%2.2x", status);
|
||||
}
|
||||
|
||||
void hci_update_background_scan(struct hci_dev *hdev)
|
||||
{
|
||||
int err;
|
||||
struct hci_request req;
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
__hci_update_background_scan(&req);
|
||||
|
||||
err = hci_req_run(&req, update_background_scan_complete);
|
||||
if (err && err != -ENODATA)
|
||||
BT_ERR("Failed to run HCI request: err %d", err);
|
||||
}
|
54
net/bluetooth/hci_request.h
Normal file
54
net/bluetooth/hci_request.h
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
struct hci_request {
|
||||
struct hci_dev *hdev;
|
||||
struct sk_buff_head cmd_q;
|
||||
|
||||
/* If something goes wrong when building the HCI request, the error
|
||||
* value is stored in this field.
|
||||
*/
|
||||
int err;
|
||||
};
|
||||
|
||||
void hci_req_init(struct hci_request *req, struct hci_dev *hdev);
|
||||
int hci_req_run(struct hci_request *req, hci_req_complete_t complete);
|
||||
void hci_req_add(struct hci_request *req, u16 opcode, u32 plen,
|
||||
const void *param);
|
||||
void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen,
|
||||
const void *param, u8 event);
|
||||
void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status);
|
||||
|
||||
struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen,
|
||||
const void *param);
|
||||
|
||||
void hci_req_add_le_scan_disable(struct hci_request *req);
|
||||
void hci_req_add_le_passive_scan(struct hci_request *req);
|
||||
|
||||
void hci_update_page_scan(struct hci_dev *hdev);
|
||||
void __hci_update_page_scan(struct hci_request *req);
|
||||
|
||||
int hci_update_random_address(struct hci_request *req, bool require_privacy,
|
||||
u8 *own_addr_type);
|
||||
|
||||
void hci_update_background_scan(struct hci_dev *hdev);
|
||||
void __hci_update_background_scan(struct hci_request *req);
|
|
@ -32,6 +32,7 @@
|
|||
#include <net/bluetooth/l2cap.h>
|
||||
#include <net/bluetooth/mgmt.h>
|
||||
|
||||
#include "hci_request.h"
|
||||
#include "smp.h"
|
||||
|
||||
#define MGMT_VERSION 1
|
||||
|
@ -138,7 +139,7 @@ struct pending_cmd {
|
|||
size_t param_len;
|
||||
struct sock *sk;
|
||||
void *user_data;
|
||||
void (*cmd_complete)(struct pending_cmd *cmd, u8 status);
|
||||
int (*cmd_complete)(struct pending_cmd *cmd, u8 status);
|
||||
};
|
||||
|
||||
/* HCI to MGMT error code conversion table */
|
||||
|
@ -1486,16 +1487,16 @@ static void cmd_complete_rsp(struct pending_cmd *cmd, void *data)
|
|||
cmd_status_rsp(cmd, data);
|
||||
}
|
||||
|
||||
static void generic_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
static int generic_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
{
|
||||
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
|
||||
cmd->param_len);
|
||||
return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
|
||||
cmd->param, cmd->param_len);
|
||||
}
|
||||
|
||||
static void addr_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
static int addr_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
{
|
||||
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
|
||||
sizeof(struct mgmt_addr_info));
|
||||
return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param,
|
||||
sizeof(struct mgmt_addr_info));
|
||||
}
|
||||
|
||||
static u8 mgmt_bredr_support(struct hci_dev *hdev)
|
||||
|
@ -1566,7 +1567,7 @@ static void set_discoverable_complete(struct hci_dev *hdev, u8 status)
|
|||
* entries.
|
||||
*/
|
||||
hci_req_init(&req, hdev);
|
||||
hci_update_page_scan(hdev, &req);
|
||||
__hci_update_page_scan(&req);
|
||||
update_class(&req);
|
||||
hci_req_run(&req, NULL);
|
||||
|
||||
|
@ -1813,7 +1814,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status)
|
|||
|
||||
if (conn_changed || discov_changed) {
|
||||
new_settings(hdev, cmd->sk);
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
hci_update_page_scan(hdev);
|
||||
if (discov_changed)
|
||||
mgmt_update_adv_data(hdev);
|
||||
hci_update_background_scan(hdev);
|
||||
|
@ -1847,7 +1848,7 @@ static int set_connectable_update_settings(struct hci_dev *hdev,
|
|||
return err;
|
||||
|
||||
if (changed) {
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
hci_update_page_scan(hdev);
|
||||
hci_update_background_scan(hdev);
|
||||
return new_settings(hdev, sk);
|
||||
}
|
||||
|
@ -2227,9 +2228,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status)
|
|||
hci_req_init(&req, hdev);
|
||||
update_adv_data(&req);
|
||||
update_scan_rsp_data(&req);
|
||||
__hci_update_background_scan(&req);
|
||||
hci_req_run(&req, NULL);
|
||||
|
||||
hci_update_background_scan(hdev);
|
||||
}
|
||||
|
||||
unlock:
|
||||
|
@ -3098,16 +3098,17 @@ static struct pending_cmd *find_pairing(struct hci_conn *conn)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static void pairing_complete(struct pending_cmd *cmd, u8 status)
|
||||
static int pairing_complete(struct pending_cmd *cmd, u8 status)
|
||||
{
|
||||
struct mgmt_rp_pair_device rp;
|
||||
struct hci_conn *conn = cmd->user_data;
|
||||
int err;
|
||||
|
||||
bacpy(&rp.addr.bdaddr, &conn->dst);
|
||||
rp.addr.type = link_to_bdaddr(conn->type, conn->dst_type);
|
||||
|
||||
cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
|
||||
&rp, sizeof(rp));
|
||||
err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, status,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
/* So we don't get further callbacks for this connection */
|
||||
conn->connect_cfm_cb = NULL;
|
||||
|
@ -3122,6 +3123,8 @@ static void pairing_complete(struct pending_cmd *cmd, u8 status)
|
|||
clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags);
|
||||
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void mgmt_smp_complete(struct hci_conn *conn, bool complete)
|
||||
|
@ -3947,9 +3950,10 @@ failed:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
static int service_discovery_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
{
|
||||
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, cmd->param, 1);
|
||||
return cmd_complete(cmd->sk, cmd->index, cmd->opcode, status,
|
||||
cmd->param, 1);
|
||||
}
|
||||
|
||||
static int start_service_discovery(struct sock *sk, struct hci_dev *hdev,
|
||||
|
@ -4697,7 +4701,7 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len)
|
|||
hci_req_init(&req, hdev);
|
||||
|
||||
write_fast_connectable(&req, false);
|
||||
hci_update_page_scan(hdev, &req);
|
||||
__hci_update_page_scan(&req);
|
||||
|
||||
/* Since only the advertising data flags will change, there
|
||||
* is no need to update the scan response data.
|
||||
|
@ -5091,10 +5095,11 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
|
|||
return err;
|
||||
}
|
||||
|
||||
static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
static int conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
{
|
||||
struct hci_conn *conn = cmd->user_data;
|
||||
struct mgmt_rp_get_conn_info rp;
|
||||
int err;
|
||||
|
||||
memcpy(&rp.addr, cmd->param, sizeof(rp.addr));
|
||||
|
||||
|
@ -5108,11 +5113,13 @@ static void conn_info_cmd_complete(struct pending_cmd *cmd, u8 status)
|
|||
rp.max_tx_power = HCI_TX_POWER_INVALID;
|
||||
}
|
||||
|
||||
cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
|
||||
&rp, sizeof(rp));
|
||||
err = cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, status,
|
||||
&rp, sizeof(rp));
|
||||
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status)
|
||||
|
@ -5286,11 +5293,12 @@ unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
static int clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
|
||||
{
|
||||
struct hci_conn *conn = cmd->user_data;
|
||||
struct mgmt_rp_get_clock_info rp;
|
||||
struct hci_dev *hdev;
|
||||
int err;
|
||||
|
||||
memset(&rp, 0, sizeof(rp));
|
||||
memcpy(&rp.addr, &cmd->param, sizeof(rp.addr));
|
||||
|
@ -5310,12 +5318,15 @@ static void clock_info_cmd_complete(struct pending_cmd *cmd, u8 status)
|
|||
}
|
||||
|
||||
complete:
|
||||
cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, sizeof(rp));
|
||||
err = cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp,
|
||||
sizeof(rp));
|
||||
|
||||
if (conn) {
|
||||
hci_conn_drop(conn);
|
||||
hci_conn_put(conn);
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void get_clock_info_complete(struct hci_dev *hdev, u8 status)
|
||||
|
@ -5425,6 +5436,65 @@ unlock:
|
|||
return err;
|
||||
}
|
||||
|
||||
static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type)
|
||||
{
|
||||
struct hci_conn *conn;
|
||||
|
||||
conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr);
|
||||
if (!conn)
|
||||
return false;
|
||||
|
||||
if (conn->dst_type != type)
|
||||
return false;
|
||||
|
||||
if (conn->state != BT_CONNECTED)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static int hci_conn_params_set(struct hci_request *req, bdaddr_t *addr,
|
||||
u8 addr_type, u8 auto_connect)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_conn_params *params;
|
||||
|
||||
params = hci_conn_params_add(hdev, addr, addr_type);
|
||||
if (!params)
|
||||
return -EIO;
|
||||
|
||||
if (params->auto_connect == auto_connect)
|
||||
return 0;
|
||||
|
||||
list_del_init(¶ms->action);
|
||||
|
||||
switch (auto_connect) {
|
||||
case HCI_AUTO_CONN_DISABLED:
|
||||
case HCI_AUTO_CONN_LINK_LOSS:
|
||||
__hci_update_background_scan(req);
|
||||
break;
|
||||
case HCI_AUTO_CONN_REPORT:
|
||||
list_add(¶ms->action, &hdev->pend_le_reports);
|
||||
__hci_update_background_scan(req);
|
||||
break;
|
||||
case HCI_AUTO_CONN_DIRECT:
|
||||
case HCI_AUTO_CONN_ALWAYS:
|
||||
if (!is_connected(hdev, addr, addr_type)) {
|
||||
list_add(¶ms->action, &hdev->pend_le_conns);
|
||||
__hci_update_background_scan(req);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
params->auto_connect = auto_connect;
|
||||
|
||||
BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
|
||||
auto_connect);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void device_added(struct sock *sk, struct hci_dev *hdev,
|
||||
bdaddr_t *bdaddr, u8 type, u8 action)
|
||||
{
|
||||
|
@ -5437,10 +5507,31 @@ static void device_added(struct sock *sk, struct hci_dev *hdev,
|
|||
mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk);
|
||||
}
|
||||
|
||||
static void add_device_complete(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct pending_cmd *cmd;
|
||||
|
||||
BT_DBG("status 0x%02x", status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = mgmt_pending_find(MGMT_OP_ADD_DEVICE, hdev);
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
cmd->cmd_complete(cmd, mgmt_status(status));
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int add_device(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_add_device *cp = data;
|
||||
struct pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
u8 auto_conn, addr_type;
|
||||
int err;
|
||||
|
||||
|
@ -5457,14 +5548,24 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
|
|||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
cmd->cmd_complete = addr_cmd_complete;
|
||||
|
||||
if (cp->addr.type == BDADDR_BREDR) {
|
||||
/* Only incoming connections action is supported for now */
|
||||
if (cp->action != 0x01) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
@ -5473,7 +5574,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
|
|||
if (err)
|
||||
goto unlock;
|
||||
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
__hci_update_page_scan(&req);
|
||||
|
||||
goto added;
|
||||
}
|
||||
|
@ -5493,19 +5594,25 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
|
|||
/* If the connection parameters don't exist for this device,
|
||||
* they will be created and configured with defaults.
|
||||
*/
|
||||
if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
|
||||
if (hci_conn_params_set(&req, &cp->addr.bdaddr, addr_type,
|
||||
auto_conn) < 0) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
|
||||
MGMT_STATUS_FAILED,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd, MGMT_STATUS_FAILED);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
added:
|
||||
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
|
||||
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
|
||||
MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
|
||||
err = hci_req_run(&req, add_device_complete);
|
||||
if (err < 0) {
|
||||
/* ENODATA means no HCI commands were needed (e.g. if
|
||||
* the adapter is powered off).
|
||||
*/
|
||||
if (err == -ENODATA)
|
||||
err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
|
||||
mgmt_pending_remove(cmd);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -5523,24 +5630,55 @@ static void device_removed(struct sock *sk, struct hci_dev *hdev,
|
|||
mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk);
|
||||
}
|
||||
|
||||
static void remove_device_complete(struct hci_dev *hdev, u8 status)
|
||||
{
|
||||
struct pending_cmd *cmd;
|
||||
|
||||
BT_DBG("status 0x%02x", status);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = mgmt_pending_find(MGMT_OP_REMOVE_DEVICE, hdev);
|
||||
if (!cmd)
|
||||
goto unlock;
|
||||
|
||||
cmd->cmd_complete(cmd, mgmt_status(status));
|
||||
mgmt_pending_remove(cmd);
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
}
|
||||
|
||||
static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
||||
void *data, u16 len)
|
||||
{
|
||||
struct mgmt_cp_remove_device *cp = data;
|
||||
struct pending_cmd *cmd;
|
||||
struct hci_request req;
|
||||
int err;
|
||||
|
||||
BT_DBG("%s", hdev->name);
|
||||
|
||||
hci_req_init(&req, hdev);
|
||||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_DEVICE, hdev, data, len);
|
||||
if (!cmd) {
|
||||
err = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
cmd->cmd_complete = addr_cmd_complete;
|
||||
|
||||
if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
|
||||
struct hci_conn_params *params;
|
||||
u8 addr_type;
|
||||
|
||||
if (!bdaddr_type_is_valid(cp->addr.type)) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
@ -5549,14 +5687,13 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
&cp->addr.bdaddr,
|
||||
cp->addr.type);
|
||||
if (err) {
|
||||
err = cmd_complete(sk, hdev->id,
|
||||
MGMT_OP_REMOVE_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
__hci_update_page_scan(&req);
|
||||
|
||||
device_removed(sk, hdev, &cp->addr.bdaddr,
|
||||
cp->addr.type);
|
||||
|
@ -5571,23 +5708,23 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
|
||||
addr_type);
|
||||
if (!params) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
if (params->auto_connect == HCI_AUTO_CONN_DISABLED) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
list_del(¶ms->action);
|
||||
list_del(¶ms->list);
|
||||
kfree(params);
|
||||
hci_update_background_scan(hdev);
|
||||
__hci_update_background_scan(&req);
|
||||
|
||||
device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type);
|
||||
} else {
|
||||
|
@ -5595,9 +5732,9 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
struct bdaddr_list *b, *btmp;
|
||||
|
||||
if (cp->addr.type) {
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
|
||||
MGMT_STATUS_INVALID_PARAMS,
|
||||
&cp->addr, sizeof(cp->addr));
|
||||
err = cmd->cmd_complete(cmd,
|
||||
MGMT_STATUS_INVALID_PARAMS);
|
||||
mgmt_pending_remove(cmd);
|
||||
goto unlock;
|
||||
}
|
||||
|
||||
|
@ -5607,7 +5744,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
kfree(b);
|
||||
}
|
||||
|
||||
hci_update_page_scan(hdev, NULL);
|
||||
__hci_update_page_scan(&req);
|
||||
|
||||
list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) {
|
||||
if (p->auto_connect == HCI_AUTO_CONN_DISABLED)
|
||||
|
@ -5620,12 +5757,19 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev,
|
|||
|
||||
BT_DBG("All LE connection parameters were removed");
|
||||
|
||||
hci_update_background_scan(hdev);
|
||||
__hci_update_background_scan(&req);
|
||||
}
|
||||
|
||||
complete:
|
||||
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
|
||||
MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
|
||||
err = hci_req_run(&req, remove_device_complete);
|
||||
if (err < 0) {
|
||||
/* ENODATA means no HCI commands were needed (e.g. if
|
||||
* the adapter is powered off).
|
||||
*/
|
||||
if (err == -ENODATA)
|
||||
err = cmd->cmd_complete(cmd, MGMT_STATUS_SUCCESS);
|
||||
mgmt_pending_remove(cmd);
|
||||
}
|
||||
|
||||
unlock:
|
||||
hci_dev_unlock(hdev);
|
||||
|
@ -6037,8 +6181,9 @@ void mgmt_index_removed(struct hci_dev *hdev)
|
|||
}
|
||||
|
||||
/* This function requires the caller holds hdev->lock */
|
||||
static void restart_le_actions(struct hci_dev *hdev)
|
||||
static void restart_le_actions(struct hci_request *req)
|
||||
{
|
||||
struct hci_dev *hdev = req->hdev;
|
||||
struct hci_conn_params *p;
|
||||
|
||||
list_for_each_entry(p, &hdev->le_conn_params, list) {
|
||||
|
@ -6060,7 +6205,7 @@ static void restart_le_actions(struct hci_dev *hdev)
|
|||
}
|
||||
}
|
||||
|
||||
hci_update_background_scan(hdev);
|
||||
__hci_update_background_scan(req);
|
||||
}
|
||||
|
||||
static void powered_complete(struct hci_dev *hdev, u8 status)
|
||||
|
@ -6071,8 +6216,6 @@ static void powered_complete(struct hci_dev *hdev, u8 status)
|
|||
|
||||
hci_dev_lock(hdev);
|
||||
|
||||
restart_le_actions(hdev);
|
||||
|
||||
mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match);
|
||||
|
||||
new_settings(hdev, match.sk);
|
||||
|
@ -6130,6 +6273,8 @@ static int powered_update_hci(struct hci_dev *hdev)
|
|||
|
||||
if (test_bit(HCI_ADVERTISING, &hdev->dev_flags))
|
||||
enable_advertising(&req);
|
||||
|
||||
restart_le_actions(&req);
|
||||
}
|
||||
|
||||
link_sec = test_bit(HCI_LINK_SECURITY, &hdev->dev_flags);
|
||||
|
@ -6139,7 +6284,7 @@ static int powered_update_hci(struct hci_dev *hdev)
|
|||
|
||||
if (lmp_bredr_capable(hdev)) {
|
||||
write_fast_connectable(&req, false);
|
||||
hci_update_page_scan(hdev, &req);
|
||||
__hci_update_page_scan(&req);
|
||||
update_class(&req);
|
||||
update_name(&req);
|
||||
update_eir(&req);
|
||||
|
|
|
@ -771,7 +771,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src,
|
|||
|
||||
bacpy(&addr.l2_bdaddr, dst);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
|
||||
addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM);
|
||||
addr.l2_cid = 0;
|
||||
addr.l2_bdaddr_type = BDADDR_BREDR;
|
||||
*err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
|
||||
|
@ -2038,7 +2038,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
|
|||
/* Bind socket */
|
||||
bacpy(&addr.l2_bdaddr, ba);
|
||||
addr.l2_family = AF_BLUETOOTH;
|
||||
addr.l2_psm = cpu_to_le16(RFCOMM_PSM);
|
||||
addr.l2_psm = cpu_to_le16(L2CAP_PSM_RFCOMM);
|
||||
addr.l2_cid = 0;
|
||||
addr.l2_bdaddr_type = BDADDR_BREDR;
|
||||
err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
|
||||
|
|
244
net/bluetooth/selftest.c
Normal file
244
net/bluetooth/selftest.c
Normal file
|
@ -0,0 +1,244 @@
|
|||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
|
||||
#include "ecc.h"
|
||||
#include "smp.h"
|
||||
#include "selftest.h"
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_SELFTEST_ECDH)
|
||||
|
||||
static const u8 priv_a_1[32] __initconst = {
|
||||
0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
|
||||
0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
|
||||
0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
|
||||
0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
|
||||
};
|
||||
static const u8 priv_b_1[32] __initconst = {
|
||||
0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
|
||||
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
|
||||
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
|
||||
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55,
|
||||
};
|
||||
static const u8 pub_a_1[64] __initconst = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
||||
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
|
||||
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
||||
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
|
||||
|
||||
0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
|
||||
0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
|
||||
0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
|
||||
0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
|
||||
};
|
||||
static const u8 pub_b_1[64] __initconst = {
|
||||
0x90, 0xa1, 0xaa, 0x2f, 0xb2, 0x77, 0x90, 0x55,
|
||||
0x9f, 0xa6, 0x15, 0x86, 0xfd, 0x8a, 0xb5, 0x47,
|
||||
0x00, 0x4c, 0x9e, 0xf1, 0x84, 0x22, 0x59, 0x09,
|
||||
0x96, 0x1d, 0xaf, 0x1f, 0xf0, 0xf0, 0xa1, 0x1e,
|
||||
|
||||
0x4a, 0x21, 0xb1, 0x15, 0xf9, 0xaf, 0x89, 0x5f,
|
||||
0x76, 0x36, 0x8e, 0xe2, 0x30, 0x11, 0x2d, 0x47,
|
||||
0x60, 0x51, 0xb8, 0x9a, 0x3a, 0x70, 0x56, 0x73,
|
||||
0x37, 0xad, 0x9d, 0x42, 0x3e, 0xf3, 0x55, 0x4c,
|
||||
};
|
||||
static const u8 dhkey_1[32] __initconst = {
|
||||
0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
|
||||
0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
|
||||
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
||||
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec,
|
||||
};
|
||||
|
||||
static const u8 priv_a_2[32] __initconst = {
|
||||
0x63, 0x76, 0x45, 0xd0, 0xf7, 0x73, 0xac, 0xb7,
|
||||
0xff, 0xdd, 0x03, 0x72, 0xb9, 0x72, 0x85, 0xb4,
|
||||
0x41, 0xb6, 0x5d, 0x0c, 0x5d, 0x54, 0x84, 0x60,
|
||||
0x1a, 0xa3, 0x9a, 0x3c, 0x69, 0x16, 0xa5, 0x06,
|
||||
};
|
||||
static const u8 priv_b_2[32] __initconst = {
|
||||
0xba, 0x30, 0x55, 0x50, 0x19, 0xa2, 0xca, 0xa3,
|
||||
0xa5, 0x29, 0x08, 0xc6, 0xb5, 0x03, 0x88, 0x7e,
|
||||
0x03, 0x2b, 0x50, 0x73, 0xd4, 0x2e, 0x50, 0x97,
|
||||
0x64, 0xcd, 0x72, 0x0d, 0x67, 0xa0, 0x9a, 0x52,
|
||||
};
|
||||
static const u8 pub_a_2[64] __initconst = {
|
||||
0xdd, 0x78, 0x5c, 0x74, 0x03, 0x9b, 0x7e, 0x98,
|
||||
0xcb, 0x94, 0x87, 0x4a, 0xad, 0xfa, 0xf8, 0xd5,
|
||||
0x43, 0x3e, 0x5c, 0xaf, 0xea, 0xb5, 0x4c, 0xf4,
|
||||
0x9e, 0x80, 0x79, 0x57, 0x7b, 0xa4, 0x31, 0x2c,
|
||||
|
||||
0x4f, 0x5d, 0x71, 0x43, 0x77, 0x43, 0xf8, 0xea,
|
||||
0xd4, 0x3e, 0xbd, 0x17, 0x91, 0x10, 0x21, 0xd0,
|
||||
0x1f, 0x87, 0x43, 0x8e, 0x40, 0xe2, 0x52, 0xcd,
|
||||
0xbe, 0xdf, 0x98, 0x38, 0x18, 0x12, 0x95, 0x91,
|
||||
};
|
||||
static const u8 pub_b_2[64] __initconst = {
|
||||
0xcc, 0x00, 0x65, 0xe1, 0xf5, 0x6c, 0x0d, 0xcf,
|
||||
0xec, 0x96, 0x47, 0x20, 0x66, 0xc9, 0xdb, 0x84,
|
||||
0x81, 0x75, 0xa8, 0x4d, 0xc0, 0xdf, 0xc7, 0x9d,
|
||||
0x1b, 0x3f, 0x3d, 0xf2, 0x3f, 0xe4, 0x65, 0xf4,
|
||||
|
||||
0x79, 0xb2, 0xec, 0xd8, 0xca, 0x55, 0xa1, 0xa8,
|
||||
0x43, 0x4d, 0x6b, 0xca, 0x10, 0xb0, 0xc2, 0x01,
|
||||
0xc2, 0x33, 0x4e, 0x16, 0x24, 0xc4, 0xef, 0xee,
|
||||
0x99, 0xd8, 0xbb, 0xbc, 0x48, 0xd0, 0x01, 0x02,
|
||||
};
|
||||
static const u8 dhkey_2[32] __initconst = {
|
||||
0x69, 0xeb, 0x21, 0x32, 0xf2, 0xc6, 0x05, 0x41,
|
||||
0x60, 0x19, 0xcd, 0x5e, 0x94, 0xe1, 0xe6, 0x5f,
|
||||
0x33, 0x07, 0xe3, 0x38, 0x4b, 0x68, 0xe5, 0x62,
|
||||
0x3f, 0x88, 0x6d, 0x2f, 0x3a, 0x84, 0x85, 0xab,
|
||||
};
|
||||
|
||||
static const u8 priv_a_3[32] __initconst = {
|
||||
0xbd, 0x1a, 0x3c, 0xcd, 0xa6, 0xb8, 0x99, 0x58,
|
||||
0x99, 0xb7, 0x40, 0xeb, 0x7b, 0x60, 0xff, 0x4a,
|
||||
0x50, 0x3f, 0x10, 0xd2, 0xe3, 0xb3, 0xc9, 0x74,
|
||||
0x38, 0x5f, 0xc5, 0xa3, 0xd4, 0xf6, 0x49, 0x3f,
|
||||
};
|
||||
static const u8 pub_a_3[64] __initconst = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
||||
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
|
||||
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
||||
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20,
|
||||
|
||||
0x8b, 0xd2, 0x89, 0x15, 0xd0, 0x8e, 0x1c, 0x74,
|
||||
0x24, 0x30, 0xed, 0x8f, 0xc2, 0x45, 0x63, 0x76,
|
||||
0x5c, 0x15, 0x52, 0x5a, 0xbf, 0x9a, 0x32, 0x63,
|
||||
0x6d, 0xeb, 0x2a, 0x65, 0x49, 0x9c, 0x80, 0xdc,
|
||||
};
|
||||
static const u8 dhkey_3[32] __initconst = {
|
||||
0x2d, 0xab, 0x00, 0x48, 0xcb, 0xb3, 0x7b, 0xda,
|
||||
0x55, 0x7b, 0x8b, 0x72, 0xa8, 0x57, 0x87, 0xc3,
|
||||
0x87, 0x27, 0x99, 0x32, 0xfc, 0x79, 0x5f, 0xae,
|
||||
0x7c, 0x1c, 0xf9, 0x49, 0xe6, 0xd7, 0xaa, 0x70,
|
||||
};
|
||||
|
||||
static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32],
|
||||
const u8 pub_a[64], const u8 pub_b[64],
|
||||
const u8 dhkey[32])
|
||||
{
|
||||
u8 dhkey_a[32], dhkey_b[32];
|
||||
|
||||
ecdh_shared_secret(pub_b, priv_a, dhkey_a);
|
||||
ecdh_shared_secret(pub_a, priv_b, dhkey_b);
|
||||
|
||||
if (memcmp(dhkey_a, dhkey, 32))
|
||||
return -EINVAL;
|
||||
|
||||
if (memcmp(dhkey_b, dhkey, 32))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_ecdh(void)
|
||||
{
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
int err;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
err = test_ecdh_sample(priv_a_1, priv_b_1, pub_a_1, pub_b_1, dhkey_1);
|
||||
if (err) {
|
||||
BT_ERR("ECDH sample 1 failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_ecdh_sample(priv_a_2, priv_b_2, pub_a_2, pub_b_2, dhkey_2);
|
||||
if (err) {
|
||||
BT_ERR("ECDH sample 2 failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_ecdh_sample(priv_a_3, priv_a_3, pub_a_3, pub_a_3, dhkey_3);
|
||||
if (err) {
|
||||
BT_ERR("ECDH sample 3 failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||
|
||||
BT_INFO("ECDH test passed in %lld usecs", duration);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static inline int test_ecdh(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int __init run_selftest(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
BT_INFO("Starting self testing");
|
||||
|
||||
err = test_ecdh();
|
||||
if (err)
|
||||
goto done;
|
||||
|
||||
err = bt_selftest_smp();
|
||||
|
||||
done:
|
||||
BT_INFO("Finished self testing");
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#if IS_MODULE(CONFIG_BT)
|
||||
|
||||
/* This is run when CONFIG_BT_SELFTEST=y and CONFIG_BT=m and is just a
|
||||
* wrapper to allow running this at module init.
|
||||
*
|
||||
* If CONFIG_BT_SELFTEST=n, then this code is not compiled at all.
|
||||
*/
|
||||
int __init bt_selftest(void)
|
||||
{
|
||||
return run_selftest();
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/* This is run when CONFIG_BT_SELFTEST=y and CONFIG_BT=y and is run
|
||||
* via late_initcall() as last item in the initialization sequence.
|
||||
*
|
||||
* If CONFIG_BT_SELFTEST=n, then this code is not compiled at all.
|
||||
*/
|
||||
static int __init bt_selftest_init(void)
|
||||
{
|
||||
return run_selftest();
|
||||
}
|
||||
late_initcall(bt_selftest_init);
|
||||
|
||||
#endif
|
45
net/bluetooth/selftest.h
Normal file
45
net/bluetooth/selftest.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
BlueZ - Bluetooth protocol stack for Linux
|
||||
Copyright (C) 2014 Intel Corporation
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
published by the Free Software Foundation;
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
|
||||
CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
|
||||
ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
|
||||
COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
|
||||
SOFTWARE IS DISCLAIMED.
|
||||
*/
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_SELFTEST) && IS_MODULE(CONFIG_BT)
|
||||
|
||||
/* When CONFIG_BT_SELFTEST=y and the CONFIG_BT=m, then the self testing
|
||||
* is run at module loading time.
|
||||
*/
|
||||
int bt_selftest(void);
|
||||
|
||||
#else
|
||||
|
||||
/* When CONFIG_BT_SELFTEST=y and CONFIG_BT=y, then the self testing
|
||||
* is run via late_initcall() to make sure that subsys_initcall() of
|
||||
* the Bluetooth subsystem and device_initcall() of the Crypto subsystem
|
||||
* do not clash.
|
||||
*
|
||||
* When CONFIG_BT_SELFTEST=n, then this turns into an empty call that
|
||||
* has no impact.
|
||||
*/
|
||||
static inline int bt_selftest(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
|
@ -223,8 +223,9 @@ static int smp_f4(struct crypto_hash *tfm_cmac, const u8 u[32], const u8 v[32],
|
|||
return err;
|
||||
}
|
||||
|
||||
static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
|
||||
u8 a1[7], u8 a2[7], u8 mackey[16], u8 ltk[16])
|
||||
static int smp_f5(struct crypto_hash *tfm_cmac, const u8 w[32],
|
||||
const u8 n1[16], const u8 n2[16], const u8 a1[7],
|
||||
const u8 a2[7], u8 mackey[16], u8 ltk[16])
|
||||
{
|
||||
/* The btle, salt and length "magic" values are as defined in
|
||||
* the SMP section of the Bluetooth core specification. In ASCII
|
||||
|
@ -276,7 +277,7 @@ static int smp_f5(struct crypto_hash *tfm_cmac, u8 w[32], u8 n1[16], u8 n2[16],
|
|||
}
|
||||
|
||||
static int smp_f6(struct crypto_hash *tfm_cmac, const u8 w[16],
|
||||
const u8 n1[16], u8 n2[16], const u8 r[16],
|
||||
const u8 n1[16], const u8 n2[16], const u8 r[16],
|
||||
const u8 io_cap[3], const u8 a1[7], const u8 a2[7],
|
||||
u8 res[16])
|
||||
{
|
||||
|
@ -3021,3 +3022,331 @@ void smp_unregister(struct hci_dev *hdev)
|
|||
smp_del_chan(chan);
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
|
||||
|
||||
static int __init test_ah(struct crypto_blkcipher *tfm_aes)
|
||||
{
|
||||
const u8 irk[16] = {
|
||||
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
||||
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
|
||||
const u8 r[3] = { 0x94, 0x81, 0x70 };
|
||||
const u8 exp[3] = { 0xaa, 0xfb, 0x0d };
|
||||
u8 res[3];
|
||||
int err;
|
||||
|
||||
err = smp_ah(tfm_aes, irk, r, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(res, exp, 3))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_c1(struct crypto_blkcipher *tfm_aes)
|
||||
{
|
||||
const u8 k[16] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
const u8 r[16] = {
|
||||
0xe0, 0x2e, 0x70, 0xc6, 0x4e, 0x27, 0x88, 0x63,
|
||||
0x0e, 0x6f, 0xad, 0x56, 0x21, 0xd5, 0x83, 0x57 };
|
||||
const u8 preq[7] = { 0x01, 0x01, 0x00, 0x00, 0x10, 0x07, 0x07 };
|
||||
const u8 pres[7] = { 0x02, 0x03, 0x00, 0x00, 0x08, 0x00, 0x05 };
|
||||
const u8 _iat = 0x01;
|
||||
const u8 _rat = 0x00;
|
||||
const bdaddr_t ra = { { 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1 } };
|
||||
const bdaddr_t ia = { { 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1 } };
|
||||
const u8 exp[16] = {
|
||||
0x86, 0x3b, 0xf1, 0xbe, 0xc5, 0x4d, 0xa7, 0xd2,
|
||||
0xea, 0x88, 0x89, 0x87, 0xef, 0x3f, 0x1e, 0x1e };
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_c1(tfm_aes, k, r, preq, pres, _iat, &ia, _rat, &ra, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(res, exp, 16))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_s1(struct crypto_blkcipher *tfm_aes)
|
||||
{
|
||||
const u8 k[16] = {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
const u8 r1[16] = {
|
||||
0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 };
|
||||
const u8 r2[16] = {
|
||||
0x00, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99 };
|
||||
const u8 exp[16] = {
|
||||
0x62, 0xa0, 0x6d, 0x79, 0xae, 0x16, 0x42, 0x5b,
|
||||
0x9b, 0xf4, 0xb0, 0xe8, 0xf0, 0xe1, 0x1f, 0x9a };
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_s1(tfm_aes, k, r1, r2, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(res, exp, 16))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_f4(struct crypto_hash *tfm_cmac)
|
||||
{
|
||||
const u8 u[32] = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
||||
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
|
||||
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
||||
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
|
||||
const u8 v[32] = {
|
||||
0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
|
||||
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
|
||||
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
|
||||
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
|
||||
const u8 x[16] = {
|
||||
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
||||
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
||||
const u8 z = 0x00;
|
||||
const u8 exp[16] = {
|
||||
0x2d, 0x87, 0x74, 0xa9, 0xbe, 0xa1, 0xed, 0xf1,
|
||||
0x1c, 0xbd, 0xa9, 0x07, 0xf1, 0x16, 0xc9, 0xf2 };
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_f4(tfm_cmac, u, v, x, z, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(res, exp, 16))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_f5(struct crypto_hash *tfm_cmac)
|
||||
{
|
||||
const u8 w[32] = {
|
||||
0x98, 0xa6, 0xbf, 0x73, 0xf3, 0x34, 0x8d, 0x86,
|
||||
0xf1, 0x66, 0xf8, 0xb4, 0x13, 0x6b, 0x79, 0x99,
|
||||
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
||||
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
|
||||
const u8 n1[16] = {
|
||||
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
||||
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
||||
const u8 n2[16] = {
|
||||
0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
|
||||
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
|
||||
const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 };
|
||||
const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 };
|
||||
const u8 exp_ltk[16] = {
|
||||
0x38, 0x0a, 0x75, 0x94, 0xb5, 0x22, 0x05, 0x98,
|
||||
0x23, 0xcd, 0xd7, 0x69, 0x11, 0x79, 0x86, 0x69 };
|
||||
const u8 exp_mackey[16] = {
|
||||
0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
|
||||
0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
|
||||
u8 mackey[16], ltk[16];
|
||||
int err;
|
||||
|
||||
err = smp_f5(tfm_cmac, w, n1, n2, a1, a2, mackey, ltk);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(mackey, exp_mackey, 16))
|
||||
return -EINVAL;
|
||||
|
||||
if (memcmp(ltk, exp_ltk, 16))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_f6(struct crypto_hash *tfm_cmac)
|
||||
{
|
||||
const u8 w[16] = {
|
||||
0x20, 0x6e, 0x63, 0xce, 0x20, 0x6a, 0x3f, 0xfd,
|
||||
0x02, 0x4a, 0x08, 0xa1, 0x76, 0xf1, 0x65, 0x29 };
|
||||
const u8 n1[16] = {
|
||||
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
||||
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
||||
const u8 n2[16] = {
|
||||
0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
|
||||
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
|
||||
const u8 r[16] = {
|
||||
0xc8, 0x0f, 0x2d, 0x0c, 0xd2, 0x42, 0xda, 0x08,
|
||||
0x54, 0xbb, 0x53, 0xb4, 0x3b, 0x34, 0xa3, 0x12 };
|
||||
const u8 io_cap[3] = { 0x02, 0x01, 0x01 };
|
||||
const u8 a1[7] = { 0xce, 0xbf, 0x37, 0x37, 0x12, 0x56, 0x00 };
|
||||
const u8 a2[7] = { 0xc1, 0xcf, 0x2d, 0x70, 0x13, 0xa7, 0x00 };
|
||||
const u8 exp[16] = {
|
||||
0x61, 0x8f, 0x95, 0xda, 0x09, 0x0b, 0x6c, 0xd2,
|
||||
0xc5, 0xe8, 0xd0, 0x9c, 0x98, 0x73, 0xc4, 0xe3 };
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_f6(tfm_cmac, w, n1, n2, r, io_cap, a1, a2, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(res, exp, 16))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_g2(struct crypto_hash *tfm_cmac)
|
||||
{
|
||||
const u8 u[32] = {
|
||||
0xe6, 0x9d, 0x35, 0x0e, 0x48, 0x01, 0x03, 0xcc,
|
||||
0xdb, 0xfd, 0xf4, 0xac, 0x11, 0x91, 0xf4, 0xef,
|
||||
0xb9, 0xa5, 0xf9, 0xe9, 0xa7, 0x83, 0x2c, 0x5e,
|
||||
0x2c, 0xbe, 0x97, 0xf2, 0xd2, 0x03, 0xb0, 0x20 };
|
||||
const u8 v[32] = {
|
||||
0xfd, 0xc5, 0x7f, 0xf4, 0x49, 0xdd, 0x4f, 0x6b,
|
||||
0xfb, 0x7c, 0x9d, 0xf1, 0xc2, 0x9a, 0xcb, 0x59,
|
||||
0x2a, 0xe7, 0xd4, 0xee, 0xfb, 0xfc, 0x0a, 0x90,
|
||||
0x9a, 0xbb, 0xf6, 0x32, 0x3d, 0x8b, 0x18, 0x55 };
|
||||
const u8 x[16] = {
|
||||
0xab, 0xae, 0x2b, 0x71, 0xec, 0xb2, 0xff, 0xff,
|
||||
0x3e, 0x73, 0x77, 0xd1, 0x54, 0x84, 0xcb, 0xd5 };
|
||||
const u8 y[16] = {
|
||||
0xcf, 0xc4, 0x3d, 0xff, 0xf7, 0x83, 0x65, 0x21,
|
||||
0x6e, 0x5f, 0xa7, 0x25, 0xcc, 0xe7, 0xe8, 0xa6 };
|
||||
const u32 exp_val = 0x2f9ed5ba % 1000000;
|
||||
u32 val;
|
||||
int err;
|
||||
|
||||
err = smp_g2(tfm_cmac, u, v, x, y, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val != exp_val)
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init test_h6(struct crypto_hash *tfm_cmac)
|
||||
{
|
||||
const u8 w[16] = {
|
||||
0x9b, 0x7d, 0x39, 0x0a, 0xa6, 0x10, 0x10, 0x34,
|
||||
0x05, 0xad, 0xc8, 0x57, 0xa3, 0x34, 0x02, 0xec };
|
||||
const u8 key_id[4] = { 0x72, 0x62, 0x65, 0x6c };
|
||||
const u8 exp[16] = {
|
||||
0x99, 0x63, 0xb1, 0x80, 0xe2, 0xa9, 0xd3, 0xe8,
|
||||
0x1c, 0xc9, 0x6d, 0xe7, 0x02, 0xe1, 0x9a, 0x2d };
|
||||
u8 res[16];
|
||||
int err;
|
||||
|
||||
err = smp_h6(tfm_cmac, w, key_id, res);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (memcmp(res, exp, 16))
|
||||
return -EINVAL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init run_selftests(struct crypto_blkcipher *tfm_aes,
|
||||
struct crypto_hash *tfm_cmac)
|
||||
{
|
||||
ktime_t calltime, delta, rettime;
|
||||
unsigned long long duration;
|
||||
int err;
|
||||
|
||||
calltime = ktime_get();
|
||||
|
||||
err = test_ah(tfm_aes);
|
||||
if (err) {
|
||||
BT_ERR("smp_ah test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_c1(tfm_aes);
|
||||
if (err) {
|
||||
BT_ERR("smp_c1 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_s1(tfm_aes);
|
||||
if (err) {
|
||||
BT_ERR("smp_s1 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_f4(tfm_cmac);
|
||||
if (err) {
|
||||
BT_ERR("smp_f4 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_f5(tfm_cmac);
|
||||
if (err) {
|
||||
BT_ERR("smp_f5 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_f6(tfm_cmac);
|
||||
if (err) {
|
||||
BT_ERR("smp_f6 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_g2(tfm_cmac);
|
||||
if (err) {
|
||||
BT_ERR("smp_g2 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
err = test_h6(tfm_cmac);
|
||||
if (err) {
|
||||
BT_ERR("smp_h6 test failed");
|
||||
return err;
|
||||
}
|
||||
|
||||
rettime = ktime_get();
|
||||
delta = ktime_sub(rettime, calltime);
|
||||
duration = (unsigned long long) ktime_to_ns(delta) >> 10;
|
||||
|
||||
BT_INFO("SMP test passed in %lld usecs", duration);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init bt_selftest_smp(void)
|
||||
{
|
||||
struct crypto_blkcipher *tfm_aes;
|
||||
struct crypto_hash *tfm_cmac;
|
||||
int err;
|
||||
|
||||
tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm_aes)) {
|
||||
BT_ERR("Unable to create ECB crypto context");
|
||||
return PTR_ERR(tfm_aes);
|
||||
}
|
||||
|
||||
tfm_cmac = crypto_alloc_hash("cmac(aes)", 0, CRYPTO_ALG_ASYNC);
|
||||
if (IS_ERR(tfm_cmac)) {
|
||||
BT_ERR("Unable to create CMAC crypto context");
|
||||
crypto_free_blkcipher(tfm_aes);
|
||||
return PTR_ERR(tfm_cmac);
|
||||
}
|
||||
|
||||
err = run_selftests(tfm_aes, tfm_cmac);
|
||||
|
||||
crypto_free_hash(tfm_cmac);
|
||||
crypto_free_blkcipher(tfm_aes);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -192,4 +192,17 @@ int smp_generate_rpa(struct hci_dev *hdev, const u8 irk[16], bdaddr_t *rpa);
|
|||
int smp_register(struct hci_dev *hdev);
|
||||
void smp_unregister(struct hci_dev *hdev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_BT_SELFTEST_SMP)
|
||||
|
||||
int bt_selftest_smp(void);
|
||||
|
||||
#else
|
||||
|
||||
static inline int bt_selftest_smp(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* __SMP_H */
|
||||
|
|
|
@ -121,7 +121,7 @@ static int ieee802154_nl_fill_iface(struct sk_buff *msg, u32 portid,
|
|||
params.transmit_power) ||
|
||||
nla_put_u8(msg, IEEE802154_ATTR_LBT_ENABLED, params.lbt) ||
|
||||
nla_put_u8(msg, IEEE802154_ATTR_CCA_MODE,
|
||||
params.cca_mode) ||
|
||||
params.cca.mode) ||
|
||||
nla_put_s32(msg, IEEE802154_ATTR_CCA_ED_LEVEL,
|
||||
params.cca_ed_level) ||
|
||||
nla_put_u8(msg, IEEE802154_ATTR_CSMA_RETRIES,
|
||||
|
@ -516,7 +516,7 @@ int ieee802154_set_macparams(struct sk_buff *skb, struct genl_info *info)
|
|||
params.lbt = nla_get_u8(info->attrs[IEEE802154_ATTR_LBT_ENABLED]);
|
||||
|
||||
if (info->attrs[IEEE802154_ATTR_CCA_MODE])
|
||||
params.cca_mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
|
||||
params.cca.mode = nla_get_u8(info->attrs[IEEE802154_ATTR_CCA_MODE]);
|
||||
|
||||
if (info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL])
|
||||
params.cca_ed_level = nla_get_s32(info->attrs[IEEE802154_ATTR_CCA_ED_LEVEL]);
|
||||
|
|
|
@ -209,7 +209,8 @@ static const struct nla_policy nl802154_policy[NL802154_ATTR_MAX+1] = {
|
|||
|
||||
[NL802154_ATTR_TX_POWER] = { .type = NLA_S8, },
|
||||
|
||||
[NL802154_ATTR_CCA_MODE] = { .type = NLA_U8, },
|
||||
[NL802154_ATTR_CCA_MODE] = { .type = NLA_U32, },
|
||||
[NL802154_ATTR_CCA_OPT] = { .type = NLA_U32, },
|
||||
|
||||
[NL802154_ATTR_SUPPORTED_CHANNEL] = { .type = NLA_U32, },
|
||||
|
||||
|
@ -290,10 +291,16 @@ static int nl802154_send_wpan_phy(struct cfg802154_registered_device *rdev,
|
|||
goto nla_put_failure;
|
||||
|
||||
/* cca mode */
|
||||
if (nla_put_u8(msg, NL802154_ATTR_CCA_MODE,
|
||||
rdev->wpan_phy.cca_mode))
|
||||
if (nla_put_u32(msg, NL802154_ATTR_CCA_MODE,
|
||||
rdev->wpan_phy.cca.mode))
|
||||
goto nla_put_failure;
|
||||
|
||||
if (rdev->wpan_phy.cca.mode == NL802154_CCA_ENERGY_CARRIER) {
|
||||
if (nla_put_u32(msg, NL802154_ATTR_CCA_OPT,
|
||||
rdev->wpan_phy.cca.opt))
|
||||
goto nla_put_failure;
|
||||
}
|
||||
|
||||
if (nla_put_s8(msg, NL802154_ATTR_TX_POWER,
|
||||
rdev->wpan_phy.transmit_power))
|
||||
goto nla_put_failure;
|
||||
|
@ -622,6 +629,31 @@ static int nl802154_set_channel(struct sk_buff *skb, struct genl_info *info)
|
|||
return rdev_set_channel(rdev, page, channel);
|
||||
}
|
||||
|
||||
static int nl802154_set_cca_mode(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
struct wpan_phy_cca cca;
|
||||
|
||||
if (!info->attrs[NL802154_ATTR_CCA_MODE])
|
||||
return -EINVAL;
|
||||
|
||||
cca.mode = nla_get_u32(info->attrs[NL802154_ATTR_CCA_MODE]);
|
||||
/* checking 802.15.4 constraints */
|
||||
if (cca.mode < NL802154_CCA_ENERGY || cca.mode > NL802154_CCA_ATTR_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
if (cca.mode == NL802154_CCA_ENERGY_CARRIER) {
|
||||
if (!info->attrs[NL802154_ATTR_CCA_OPT])
|
||||
return -EINVAL;
|
||||
|
||||
cca.opt = nla_get_u32(info->attrs[NL802154_ATTR_CCA_OPT]);
|
||||
if (cca.opt > NL802154_CCA_OPT_ATTR_MAX)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return rdev_set_cca_mode(rdev, &cca);
|
||||
}
|
||||
|
||||
static int nl802154_set_pan_id(struct sk_buff *skb, struct genl_info *info)
|
||||
{
|
||||
struct cfg802154_registered_device *rdev = info->user_ptr[0];
|
||||
|
@ -894,6 +926,14 @@ static const struct genl_ops nl802154_ops[] = {
|
|||
.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_CCA_MODE,
|
||||
.doit = nl802154_set_cca_mode,
|
||||
.policy = nl802154_policy,
|
||||
.flags = GENL_ADMIN_PERM,
|
||||
.internal_flags = NL802154_FLAG_NEED_WPAN_PHY |
|
||||
NL802154_FLAG_NEED_RTNL,
|
||||
},
|
||||
{
|
||||
.cmd = NL802154_CMD_SET_PAN_ID,
|
||||
.doit = nl802154_set_pan_id,
|
||||
|
|
|
@ -41,6 +41,13 @@ rdev_set_channel(struct cfg802154_registered_device *rdev, u8 page, u8 channel)
|
|||
return rdev->ops->set_channel(&rdev->wpan_phy, page, channel);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_cca_mode(struct cfg802154_registered_device *rdev,
|
||||
const struct wpan_phy_cca *cca)
|
||||
{
|
||||
return rdev->ops->set_cca_mode(&rdev->wpan_phy, cca);
|
||||
}
|
||||
|
||||
static inline int
|
||||
rdev_set_pan_id(struct cfg802154_registered_device *rdev,
|
||||
struct wpan_dev *wpan_dev, __le16 pan_id)
|
||||
|
|
|
@ -68,7 +68,7 @@ static DEVICE_ATTR_RO(name)
|
|||
MASTER_SHOW(current_channel, "%d");
|
||||
MASTER_SHOW(current_page, "%d");
|
||||
MASTER_SHOW(transmit_power, "%d +- 1 dB");
|
||||
MASTER_SHOW(cca_mode, "%d");
|
||||
MASTER_SHOW_COMPLEX(cca_mode, "%d", phy->cca.mode);
|
||||
|
||||
static ssize_t channels_supported_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
|
|
|
@ -86,6 +86,26 @@ ieee802154_set_channel(struct wpan_phy *wpan_phy, u8 page, u8 channel)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_cca_mode(struct wpan_phy *wpan_phy,
|
||||
const struct wpan_phy_cca *cca)
|
||||
{
|
||||
struct ieee802154_local *local = wpan_phy_priv(wpan_phy);
|
||||
int ret;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
/* check if phy support this setting */
|
||||
if (!(local->hw.flags & IEEE802154_HW_CCA_MODE))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = drv_set_cca_mode(local, cca);
|
||||
if (!ret)
|
||||
wpan_phy->cca = *cca;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_set_pan_id(struct wpan_phy *wpan_phy, struct wpan_dev *wpan_dev,
|
||||
__le16 pan_id)
|
||||
|
@ -201,6 +221,7 @@ const struct cfg802154_ops mac802154_config_ops = {
|
|||
.add_virtual_intf = ieee802154_add_iface,
|
||||
.del_virtual_intf = ieee802154_del_iface,
|
||||
.set_channel = ieee802154_set_channel,
|
||||
.set_cca_mode = ieee802154_set_cca_mode,
|
||||
.set_pan_id = ieee802154_set_pan_id,
|
||||
.set_short_addr = ieee802154_set_short_addr,
|
||||
.set_backoff_exponent = ieee802154_set_backoff_exponent,
|
||||
|
|
|
@ -70,7 +70,8 @@ static inline int drv_set_tx_power(struct ieee802154_local *local, s8 dbm)
|
|||
return local->ops->set_txpower(&local->hw, dbm);
|
||||
}
|
||||
|
||||
static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
|
||||
static inline int drv_set_cca_mode(struct ieee802154_local *local,
|
||||
const struct wpan_phy_cca *cca)
|
||||
{
|
||||
might_sleep();
|
||||
|
||||
|
@ -79,7 +80,7 @@ static inline int drv_set_cca_mode(struct ieee802154_local *local, u8 cca_mode)
|
|||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return local->ops->set_cca_mode(&local->hw, cca_mode);
|
||||
return local->ops->set_cca_mode(&local->hw, cca);
|
||||
}
|
||||
|
||||
static inline int drv_set_lbt_mode(struct ieee802154_local *local, bool mode)
|
||||
|
|
|
@ -137,25 +137,11 @@ static int mac802154_wpan_mac_addr(struct net_device *dev, void *p)
|
|||
static int mac802154_slave_open(struct net_device *dev)
|
||||
{
|
||||
struct ieee802154_sub_if_data *sdata = IEEE802154_DEV_TO_SUB_IF(dev);
|
||||
struct ieee802154_sub_if_data *subif;
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
int res = 0;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (sdata->vif.type == NL802154_IFTYPE_NODE) {
|
||||
mutex_lock(&sdata->local->iflist_mtx);
|
||||
list_for_each_entry(subif, &sdata->local->interfaces, list) {
|
||||
if (subif != sdata &&
|
||||
subif->vif.type == sdata->vif.type &&
|
||||
ieee802154_sdata_running(subif)) {
|
||||
mutex_unlock(&sdata->local->iflist_mtx);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&sdata->local->iflist_mtx);
|
||||
}
|
||||
|
||||
set_bit(SDATA_STATE_RUNNING, &sdata->state);
|
||||
|
||||
if (!local->open_count) {
|
||||
|
@ -175,6 +161,88 @@ err:
|
|||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_check_mac_settings(struct ieee802154_local *local,
|
||||
struct wpan_dev *wpan_dev,
|
||||
struct wpan_dev *nwpan_dev)
|
||||
{
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_PROMISCUOUS) {
|
||||
if (wpan_dev->promiscuous_mode != nwpan_dev->promiscuous_mode)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_AFILT) {
|
||||
if (wpan_dev->pan_id != nwpan_dev->pan_id)
|
||||
return -EBUSY;
|
||||
|
||||
if (wpan_dev->short_addr != nwpan_dev->short_addr)
|
||||
return -EBUSY;
|
||||
|
||||
if (wpan_dev->extended_addr != nwpan_dev->extended_addr)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_CSMA_PARAMS) {
|
||||
if (wpan_dev->min_be != nwpan_dev->min_be)
|
||||
return -EBUSY;
|
||||
|
||||
if (wpan_dev->max_be != nwpan_dev->max_be)
|
||||
return -EBUSY;
|
||||
|
||||
if (wpan_dev->csma_retries != nwpan_dev->csma_retries)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_FRAME_RETRIES) {
|
||||
if (wpan_dev->frame_retries != nwpan_dev->frame_retries)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_LBT) {
|
||||
if (wpan_dev->lbt != nwpan_dev->lbt)
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ieee802154_check_concurrent_iface(struct ieee802154_sub_if_data *sdata,
|
||||
enum nl802154_iftype iftype)
|
||||
{
|
||||
struct ieee802154_local *local = sdata->local;
|
||||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
struct ieee802154_sub_if_data *nsdata;
|
||||
|
||||
/* we hold the RTNL here so can safely walk the list */
|
||||
list_for_each_entry(nsdata, &local->interfaces, list) {
|
||||
if (nsdata != sdata && ieee802154_sdata_running(nsdata)) {
|
||||
int ret;
|
||||
|
||||
/* TODO currently we don't support multiple node types
|
||||
* we need to run skb_clone at rx path. Check if there
|
||||
* 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)
|
||||
return -EBUSY;
|
||||
|
||||
/* check all phy mac sublayer settings are the same.
|
||||
* We have only one phy, different values makes trouble.
|
||||
*/
|
||||
ret = ieee802154_check_mac_settings(local, wpan_dev,
|
||||
&nsdata->wpan_dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mac802154_wpan_open(struct net_device *dev)
|
||||
{
|
||||
int rc;
|
||||
|
@ -183,6 +251,10 @@ static int mac802154_wpan_open(struct net_device *dev)
|
|||
struct wpan_dev *wpan_dev = &sdata->wpan_dev;
|
||||
struct wpan_phy *phy = sdata->local->phy;
|
||||
|
||||
rc = ieee802154_check_concurrent_iface(sdata, sdata->vif.type);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
rc = mac802154_slave_open(dev);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
|
|
@ -81,7 +81,7 @@ static int mac802154_set_mac_params(struct net_device *dev,
|
|||
|
||||
/* PHY */
|
||||
wpan_dev->wpan_phy->transmit_power = params->transmit_power;
|
||||
wpan_dev->wpan_phy->cca_mode = params->cca_mode;
|
||||
wpan_dev->wpan_phy->cca = params->cca;
|
||||
wpan_dev->wpan_phy->cca_ed_level = params->cca_ed_level;
|
||||
|
||||
/* MAC */
|
||||
|
@ -98,7 +98,7 @@ static int mac802154_set_mac_params(struct net_device *dev,
|
|||
}
|
||||
|
||||
if (local->hw.flags & IEEE802154_HW_CCA_MODE) {
|
||||
ret = drv_set_cca_mode(local, params->cca_mode);
|
||||
ret = drv_set_cca_mode(local, ¶ms->cca);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
@ -122,7 +122,7 @@ static void mac802154_get_mac_params(struct net_device *dev,
|
|||
|
||||
/* PHY */
|
||||
params->transmit_power = wpan_dev->wpan_phy->transmit_power;
|
||||
params->cca_mode = wpan_dev->wpan_phy->cca_mode;
|
||||
params->cca = wpan_dev->wpan_phy->cca;
|
||||
params->cca_ed_level = wpan_dev->wpan_phy->cca_ed_level;
|
||||
|
||||
/* MAC */
|
||||
|
|
Loading…
Reference in a new issue