Merge branch 'for-upstream' of git://git.kernel.org/pub/scm/linux/kernel/git/bluetoot...
authorDavid S. Miller <davem@davemloft.net>
Fri, 2 Jan 2015 20:58:21 +0000 (15:58 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 2 Jan 2015 20:58:21 +0000 (15:58 -0500)
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>
37 files changed:
drivers/bluetooth/bfusb.c
drivers/bluetooth/btusb.c
drivers/net/ieee802154/at86rf230.c
drivers/net/ieee802154/cc2520.c
drivers/net/ieee802154/mrf24j40.c
include/net/bluetooth/hci.h
include/net/bluetooth/hci_core.h
include/net/bluetooth/l2cap.h
include/net/bluetooth/rfcomm.h
include/net/cfg802154.h
include/net/ieee802154_netdev.h
include/net/mac802154.h
include/net/nl802154.h
net/bluetooth/Kconfig
net/bluetooth/Makefile
net/bluetooth/af_bluetooth.c
net/bluetooth/hci_conn.c
net/bluetooth/hci_core.c
net/bluetooth/hci_debugfs.c [new file with mode: 0644]
net/bluetooth/hci_debugfs.h [new file with mode: 0644]
net/bluetooth/hci_event.c
net/bluetooth/hci_request.c [new file with mode: 0644]
net/bluetooth/hci_request.h [new file with mode: 0644]
net/bluetooth/mgmt.c
net/bluetooth/rfcomm/core.c
net/bluetooth/selftest.c [new file with mode: 0644]
net/bluetooth/selftest.h [new file with mode: 0644]
net/bluetooth/smp.c
net/bluetooth/smp.h
net/ieee802154/nl-mac.c
net/ieee802154/nl802154.c
net/ieee802154/rdev-ops.h
net/ieee802154/sysfs.c
net/mac802154/cfg.c
net/mac802154/driver-ops.h
net/mac802154/iface.c
net/mac802154/mac_cmd.c

index b2e7e94a67719945e9d9ef277aed9b694d3601d1..fcfb72e9e0ee5948bf7e4c73e8d1b056d0723dbf 100644 (file)
@@ -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);
index 19cf2cf22e879816cc89112f76c5592d47f24e38..f051a93c6cad150e5c346e12b7507823c3fb0926 100644 (file)
@@ -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);
 
index 1c0135620c62261dca42b32c3958d43c3dca3ded..80632fc59756c0645ea904a4dd1493e4bdec7197 100644 (file)
@@ -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;
+
+       /* 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, mode);
+       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;
        }
index f9df9fa86d5f0010d2d87ecbeca4a8df395c08c1..a43c8acb7268d6ef897f5a992101e0ee125231ff 100644 (file)
@@ -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;
 }
 
index a200fa16beae406757095f0f7090999b7a596b9c..fba2dfd910f7372b2aedc355039cc71e2433c008 100644 (file)
@@ -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;
index 40129b3838b2aa8d811756a61900de4ecf7e850c..1849a437f6e1aeba2414c96db45d3c18d3c224d6 100644 (file)
@@ -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;
index 3c7827005c25fafdcad5962866003ba132cafaa6..3e7e5110f29893cef41736cc48615b4136330225 100644 (file)
@@ -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);
 
index d1bb342d083f14efc11ba4992e8c7509a67deac1..2239a37530922682008d9bbe312e6cbfbe97fd4d 100644 (file)
@@ -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
index 578b83127af18545e51c26b859dfa6f80100c263..4190af53a46ad1a5f7868f4add65363e5ada9b20 100644 (file)
@@ -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)
index 7f713acfa106c9bd564b4c748a1d9b7b8978d8ae..eeda67652766a9979478de97105322b5ee6a7977 100644 (file)
@@ -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;
 
index 83bb8a73d23c0f056b10a8c9061997a7b98e02a9..94a297052442600acff26e86404fec2afbb27d5a 100644 (file)
@@ -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;
 };
 
index c823d910b46ca7da89866226ba5e1be7c7c93529..8506478117496c971f19ec07eb00625b4b847498 100644 (file)
@@ -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,
index 6dbd406ca41b94d9f4c7242b8b1d94e33bf570b8..f8b5bc997959f7ba171258bd31852c696873bc21 100644 (file)
@@ -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 */
index 29bcafc41adfd19b59b0437021d5ebd044dcef9b..7de74635a110cbbecbaaddf31ac5d39708362397 100644 (file)
@@ -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"
index a5432a6a0ae6075d13a596a9f58e2380c7452b08..8e96e30722668a8fa92663c1ee1ba17f2f74abd1 100644 (file)
@@ -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__
index 012e3b03589d8ce305deccf09189971dd86d1b39..ce22e0cfa923fb947256f8c36405b7e8bce5c888 100644 (file)
@@ -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();
index fe18825cc8a47ffba031dd1239aa98a7cea8a7cf..75240aaca101898755680f4e97a69e8bb29119f3 100644 (file)
 /* 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);
 
index 5dcacf9607e445777607e8be7492e4c74ec44b2e..5ef5221c1813fc43804b7d0e9677e792e3884f6c 100644 (file)
@@ -37,6 +37,8 @@
 #include <net/bluetooth/l2cap.h>
 #include <net/bluetooth/mgmt.h>
 
+#include "hci_request.h"
+#include "hci_debugfs.h"
 #include "smp.h"
 
 static void hci_rx_work(struct work_struct *work);
@@ -137,938 +139,6 @@ static const struct file_operations dut_mode_fops = {
        .llseek         = default_llseek,
 };
 
-static int features_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-       u8 p;
-
-       hci_dev_lock(hdev);
-       for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
-               seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
-                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
-                          hdev->features[p][0], hdev->features[p][1],
-                          hdev->features[p][2], hdev->features[p][3],
-                          hdev->features[p][4], hdev->features[p][5],
-                          hdev->features[p][6], hdev->features[p][7]);
-       }
-       if (lmp_le_capable(hdev))
-               seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
-                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
-                          hdev->le_features[0], hdev->le_features[1],
-                          hdev->le_features[2], hdev->le_features[3],
-                          hdev->le_features[4], hdev->le_features[5],
-                          hdev->le_features[6], hdev->le_features[7]);
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int features_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, features_show, inode->i_private);
-}
-
-static const struct file_operations features_fops = {
-       .open           = features_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int blacklist_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bdaddr_list *b;
-
-       hci_dev_lock(hdev);
-       list_for_each_entry(b, &hdev->blacklist, list)
-               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int blacklist_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, blacklist_show, inode->i_private);
-}
-
-static const struct file_operations blacklist_fops = {
-       .open           = blacklist_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int uuids_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct bt_uuid *uuid;
-
-       hci_dev_lock(hdev);
-       list_for_each_entry(uuid, &hdev->uuids, list) {
-               u8 i, val[16];
-
-               /* The Bluetooth UUID values are stored in big endian,
-                * but with reversed byte order. So convert them into
-                * the right order for the %pUb modifier.
-                */
-               for (i = 0; i < 16; i++)
-                       val[i] = uuid->uuid[15 - i];
-
-               seq_printf(f, "%pUb\n", val);
-       }
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int uuids_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, uuids_show, inode->i_private);
-}
-
-static const struct file_operations uuids_fops = {
-       .open           = uuids_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int inquiry_cache_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       struct discovery_state *cache = &hdev->discovery;
-       struct inquiry_entry *e;
-
-       hci_dev_lock(hdev);
-
-       list_for_each_entry(e, &cache->all, all) {
-               struct inquiry_data *data = &e->data;
-               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
-                          &data->bdaddr,
-                          data->pscan_rep_mode, data->pscan_period_mode,
-                          data->pscan_mode, data->dev_class[2],
-                          data->dev_class[1], data->dev_class[0],
-                          __le16_to_cpu(data->clock_offset),
-                          data->rssi, data->ssp_mode, e->timestamp);
-       }
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int inquiry_cache_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, inquiry_cache_show, inode->i_private);
-}
-
-static const struct file_operations inquiry_cache_fops = {
-       .open           = inquiry_cache_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int link_keys_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-       struct link_key *key;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(key, &hdev->link_keys, list)
-               seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
-                          HCI_LINK_KEY_SIZE, key->val, key->pin_len);
-       rcu_read_unlock();
-
-       return 0;
-}
-
-static int link_keys_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, link_keys_show, inode->i_private);
-}
-
-static const struct file_operations link_keys_fops = {
-       .open           = link_keys_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int dev_class_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-
-       hci_dev_lock(hdev);
-       seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
-                  hdev->dev_class[1], hdev->dev_class[0]);
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int dev_class_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, dev_class_show, inode->i_private);
-}
-
-static const struct file_operations dev_class_fops = {
-       .open           = dev_class_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int voice_setting_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->voice_setting;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
-                       NULL, "0x%4.4llx\n");
-
-static int auto_accept_delay_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       hdev->auto_accept_delay = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int auto_accept_delay_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->auto_accept_delay;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
-                       auto_accept_delay_set, "%llu\n");
-
-static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
-                                    size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
-
-       buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t force_sc_support_write(struct file *file,
-                                     const char __user *user_buf,
-                                     size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[32];
-       size_t buf_size = min(count, (sizeof(buf)-1));
-       bool enable;
-
-       if (test_bit(HCI_UP, &hdev->flags))
-               return -EBUSY;
-
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-
-       buf[buf_size] = '\0';
-       if (strtobool(buf, &enable))
-               return -EINVAL;
-
-       if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
-               return -EALREADY;
-
-       change_bit(HCI_FORCE_SC, &hdev->dbg_flags);
-
-       return count;
-}
-
-static const struct file_operations force_sc_support_fops = {
-       .open           = simple_open,
-       .read           = force_sc_support_read,
-       .write          = force_sc_support_write,
-       .llseek         = default_llseek,
-};
-
-static ssize_t force_lesc_support_read(struct file *file, char __user *user_buf,
-                                      size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
-
-       buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t force_lesc_support_write(struct file *file,
-                                       const char __user *user_buf,
-                                       size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[32];
-       size_t buf_size = min(count, (sizeof(buf)-1));
-       bool enable;
-
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-
-       buf[buf_size] = '\0';
-       if (strtobool(buf, &enable))
-               return -EINVAL;
-
-       if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
-               return -EALREADY;
-
-       change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
-
-       return count;
-}
-
-static const struct file_operations force_lesc_support_fops = {
-       .open           = simple_open,
-       .read           = force_lesc_support_read,
-       .write          = force_lesc_support_write,
-       .llseek         = default_llseek,
-};
-
-static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
-                                size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
-
-       buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static const struct file_operations sc_only_mode_fops = {
-       .open           = simple_open,
-       .read           = sc_only_mode_read,
-       .llseek         = default_llseek,
-};
-
-static int idle_timeout_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val != 0 && (val < 500 || val > 3600000))
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->idle_timeout = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int idle_timeout_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->idle_timeout;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
-                       idle_timeout_set, "%llu\n");
-
-static int rpa_timeout_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       /* Require the RPA timeout to be at least 30 seconds and at most
-        * 24 hours.
-        */
-       if (val < 30 || val > (60 * 60 * 24))
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->rpa_timeout = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int rpa_timeout_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->rpa_timeout;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
-                       rpa_timeout_set, "%llu\n");
-
-static int sniff_min_interval_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->sniff_min_interval = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int sniff_min_interval_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->sniff_min_interval;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
-                       sniff_min_interval_set, "%llu\n");
-
-static int sniff_max_interval_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->sniff_max_interval = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int sniff_max_interval_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->sniff_max_interval;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
-                       sniff_max_interval_set, "%llu\n");
-
-static int conn_info_min_age_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val == 0 || val > hdev->conn_info_max_age)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->conn_info_min_age = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int conn_info_min_age_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->conn_info_min_age;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get,
-                       conn_info_min_age_set, "%llu\n");
-
-static int conn_info_max_age_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val == 0 || val < hdev->conn_info_min_age)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->conn_info_max_age = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int conn_info_max_age_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->conn_info_max_age;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
-                       conn_info_max_age_set, "%llu\n");
-
-static int identity_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-       bdaddr_t addr;
-       u8 addr_type;
-
-       hci_dev_lock(hdev);
-
-       hci_copy_identity_address(hdev, &addr, &addr_type);
-
-       seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
-                  16, hdev->irk, &hdev->rpa);
-
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int identity_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, identity_show, inode->i_private);
-}
-
-static const struct file_operations identity_fops = {
-       .open           = identity_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int random_address_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-
-       hci_dev_lock(hdev);
-       seq_printf(f, "%pMR\n", &hdev->random_addr);
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int random_address_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, random_address_show, inode->i_private);
-}
-
-static const struct file_operations random_address_fops = {
-       .open           = random_address_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int static_address_show(struct seq_file *f, void *p)
-{
-       struct hci_dev *hdev = f->private;
-
-       hci_dev_lock(hdev);
-       seq_printf(f, "%pMR\n", &hdev->static_addr);
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int static_address_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, static_address_show, inode->i_private);
-}
-
-static const struct file_operations static_address_fops = {
-       .open           = static_address_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static ssize_t force_static_address_read(struct file *file,
-                                        char __user *user_buf,
-                                        size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
-
-       buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
-}
-
-static ssize_t force_static_address_write(struct file *file,
-                                         const char __user *user_buf,
-                                         size_t count, loff_t *ppos)
-{
-       struct hci_dev *hdev = file->private_data;
-       char buf[32];
-       size_t buf_size = min(count, (sizeof(buf)-1));
-       bool enable;
-
-       if (test_bit(HCI_UP, &hdev->flags))
-               return -EBUSY;
-
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
-
-       buf[buf_size] = '\0';
-       if (strtobool(buf, &enable))
-               return -EINVAL;
-
-       if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags))
-               return -EALREADY;
-
-       change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags);
-
-       return count;
-}
-
-static const struct file_operations force_static_address_fops = {
-       .open           = simple_open,
-       .read           = force_static_address_read,
-       .write          = force_static_address_write,
-       .llseek         = default_llseek,
-};
-
-static int white_list_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-       struct bdaddr_list *b;
-
-       hci_dev_lock(hdev);
-       list_for_each_entry(b, &hdev->le_white_list, list)
-               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int white_list_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, white_list_show, inode->i_private);
-}
-
-static const struct file_operations white_list_fops = {
-       .open           = white_list_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-       struct smp_irk *irk;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
-               seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
-                          &irk->bdaddr, irk->addr_type,
-                          16, irk->val, &irk->rpa);
-       }
-       rcu_read_unlock();
-
-       return 0;
-}
-
-static int identity_resolving_keys_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, identity_resolving_keys_show,
-                          inode->i_private);
-}
-
-static const struct file_operations identity_resolving_keys_fops = {
-       .open           = identity_resolving_keys_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int long_term_keys_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-       struct smp_ltk *ltk;
-
-       rcu_read_lock();
-       list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
-               seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
-                          &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
-                          ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
-                          __le64_to_cpu(ltk->rand), 16, ltk->val);
-       rcu_read_unlock();
-
-       return 0;
-}
-
-static int long_term_keys_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, long_term_keys_show, inode->i_private);
-}
-
-static const struct file_operations long_term_keys_fops = {
-       .open           = long_term_keys_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
-static int conn_min_interval_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_conn_min_interval = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int conn_min_interval_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_conn_min_interval;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
-                       conn_min_interval_set, "%llu\n");
-
-static int conn_max_interval_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_conn_max_interval = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int conn_max_interval_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_conn_max_interval;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
-                       conn_max_interval_set, "%llu\n");
-
-static int conn_latency_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val > 0x01f3)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_conn_latency = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int conn_latency_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_conn_latency;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
-                       conn_latency_set, "%llu\n");
-
-static int supervision_timeout_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val < 0x000a || val > 0x0c80)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_supv_timeout = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int supervision_timeout_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_supv_timeout;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
-                       supervision_timeout_set, "%llu\n");
-
-static int adv_channel_map_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val < 0x01 || val > 0x07)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_adv_channel_map = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int adv_channel_map_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_adv_channel_map;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
-                       adv_channel_map_set, "%llu\n");
-
-static int adv_min_interval_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_adv_min_interval = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int adv_min_interval_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_adv_min_interval;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get,
-                       adv_min_interval_set, "%llu\n");
-
-static int adv_max_interval_set(void *data, u64 val)
-{
-       struct hci_dev *hdev = data;
-
-       if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
-               return -EINVAL;
-
-       hci_dev_lock(hdev);
-       hdev->le_adv_max_interval = val;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int adv_max_interval_get(void *data, u64 *val)
-{
-       struct hci_dev *hdev = data;
-
-       hci_dev_lock(hdev);
-       *val = hdev->le_adv_max_interval;
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
-                       adv_max_interval_set, "%llu\n");
-
-static int device_list_show(struct seq_file *f, void *ptr)
-{
-       struct hci_dev *hdev = f->private;
-       struct hci_conn_params *p;
-       struct bdaddr_list *b;
-
-       hci_dev_lock(hdev);
-       list_for_each_entry(b, &hdev->whitelist, list)
-               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
-       list_for_each_entry(p, &hdev->le_conn_params, list) {
-               seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
-                          p->auto_connect);
-       }
-       hci_dev_unlock(hdev);
-
-       return 0;
-}
-
-static int device_list_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, device_list_show, inode->i_private);
-}
-
-static const struct file_operations device_list_fops = {
-       .open           = device_list_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-};
-
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -1553,10 +623,16 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt)
        if (lmp_le_capable(hdev))
                le_setup(req);
 
-       /* AVM Berlin (31), aka "BlueFRITZ!", doesn't support the read
-        * local supported commands HCI command.
+       /* All Bluetooth 1.2 and later controllers should support the
+        * HCI command for reading the local supported commands.
+        *
+        * Unfortunately some controllers indicate Bluetooth 1.2 support,
+        * but do not have support for this command. If that is the case,
+        * the driver can quirk the behavior and skip reading the local
+        * supported commands.
         */
-       if (hdev->manufacturer != 31 && hdev->hci_ver > BLUETOOTH_VER_1_1)
+       if (hdev->hci_ver > BLUETOOTH_VER_1_1 &&
+           !test_bit(HCI_QUIRK_BROKEN_LOCAL_COMMANDS, &hdev->quirks))
                hci_req_add(req, HCI_OP_READ_LOCAL_COMMANDS, 0, NULL);
 
        if (lmp_ssp_capable(hdev)) {
@@ -1735,6 +811,12 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                                                 * Parameter Request
                                                 */
 
+               /* If the controller supports the Data Length Extension
+                * feature, enable the corresponding event.
+                */
+               if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT)
+                       events[0] |= 0x40;      /* LE Data Length Change */
+
                /* If the controller supports Extended Scanner Filter
                 * Policies, enable the correspondig event.
                 */
@@ -1765,6 +847,14 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt)
                        hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL);
                }
 
+               if (hdev->le_features[0] & HCI_LE_DATA_LEN_EXT) {
+                       /* Read LE Maximum Data Length */
+                       hci_req_add(req, HCI_OP_LE_READ_MAX_DATA_LEN, 0, NULL);
+
+                       /* Read LE Suggested Default Data Length */
+                       hci_req_add(req, HCI_OP_LE_READ_DEF_DATA_LEN, 0, NULL);
+               }
+
                hci_set_le_support(req);
        }
 
@@ -1847,102 +937,13 @@ static int __hci_init(struct hci_dev *hdev)
        if (!test_bit(HCI_SETUP, &hdev->dev_flags))
                return 0;
 
-       debugfs_create_file("features", 0444, hdev->debugfs, hdev,
-                           &features_fops);
-       debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
-                          &hdev->manufacturer);
-       debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
-       debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
-       debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
-                           &device_list_fops);
-       debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
-                           &blacklist_fops);
-       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
-
-       debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
-                           &conn_info_min_age_fops);
-       debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
-                           &conn_info_max_age_fops);
-
-       if (lmp_bredr_capable(hdev)) {
-               debugfs_create_file("inquiry_cache", 0444, hdev->debugfs,
-                                   hdev, &inquiry_cache_fops);
-               debugfs_create_file("link_keys", 0400, hdev->debugfs,
-                                   hdev, &link_keys_fops);
-               debugfs_create_file("dev_class", 0444, hdev->debugfs,
-                                   hdev, &dev_class_fops);
-               debugfs_create_file("voice_setting", 0444, hdev->debugfs,
-                                   hdev, &voice_setting_fops);
-       }
+       hci_debugfs_create_common(hdev);
 
-       if (lmp_ssp_capable(hdev)) {
-               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
-                                   hdev, &auto_accept_delay_fops);
-               debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
-                                   hdev, &force_sc_support_fops);
-               debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
-                                   hdev, &sc_only_mode_fops);
-               if (lmp_le_capable(hdev))
-                       debugfs_create_file("force_lesc_support", 0644,
-                                           hdev->debugfs, hdev,
-                                           &force_lesc_support_fops);
-       }
-
-       if (lmp_sniff_capable(hdev)) {
-               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
-                                   hdev, &idle_timeout_fops);
-               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
-                                   hdev, &sniff_min_interval_fops);
-               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
-                                   hdev, &sniff_max_interval_fops);
-       }
+       if (lmp_bredr_capable(hdev))
+               hci_debugfs_create_bredr(hdev);
 
        if (lmp_le_capable(hdev)) {
-               debugfs_create_file("identity", 0400, hdev->debugfs,
-                                   hdev, &identity_fops);
-               debugfs_create_file("rpa_timeout", 0644, hdev->debugfs,
-                                   hdev, &rpa_timeout_fops);
-               debugfs_create_file("random_address", 0444, hdev->debugfs,
-                                   hdev, &random_address_fops);
-               debugfs_create_file("static_address", 0444, hdev->debugfs,
-                                   hdev, &static_address_fops);
-
-               /* For controllers with a public address, provide a debug
-                * option to force the usage of the configured static
-                * address. By default the public address is used.
-                */
-               if (bacmp(&hdev->bdaddr, BDADDR_ANY))
-                       debugfs_create_file("force_static_address", 0644,
-                                           hdev->debugfs, hdev,
-                                           &force_static_address_fops);
-
-               debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
-                                 &hdev->le_white_list_size);
-               debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
-                                   &white_list_fops);
-               debugfs_create_file("identity_resolving_keys", 0400,
-                                   hdev->debugfs, hdev,
-                                   &identity_resolving_keys_fops);
-               debugfs_create_file("long_term_keys", 0400, hdev->debugfs,
-                                   hdev, &long_term_keys_fops);
-               debugfs_create_file("conn_min_interval", 0644, hdev->debugfs,
-                                   hdev, &conn_min_interval_fops);
-               debugfs_create_file("conn_max_interval", 0644, hdev->debugfs,
-                                   hdev, &conn_max_interval_fops);
-               debugfs_create_file("conn_latency", 0644, hdev->debugfs,
-                                   hdev, &conn_latency_fops);
-               debugfs_create_file("supervision_timeout", 0644, hdev->debugfs,
-                                   hdev, &supervision_timeout_fops);
-               debugfs_create_file("adv_channel_map", 0644, hdev->debugfs,
-                                   hdev, &adv_channel_map_fops);
-               debugfs_create_file("adv_min_interval", 0644, hdev->debugfs,
-                                   hdev, &adv_min_interval_fops);
-               debugfs_create_file("adv_max_interval", 0644, hdev->debugfs,
-                                   hdev, &adv_max_interval_fops);
-               debugfs_create_u16("discov_interleaved_timeout", 0644,
-                                  hdev->debugfs,
-                                  &hdev->discov_interleaved_timeout);
-
+               hci_debugfs_create_le(hdev);
                smp_register(hdev);
        }
 
@@ -3654,26 +2655,9 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev,
                    params->addr_type == addr_type) {
                        return params;
                }
-       }
-
-       return NULL;
-}
-
-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;
+       return NULL;
 }
 
 /* This function requires the caller holds hdev->lock */
@@ -3731,47 +2715,6 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev,
        return params;
 }
 
-/* This function requires the caller holds hdev->lock */
-int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type,
-                       u8 auto_connect)
-{
-       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(&params->action);
-
-       switch (auto_connect) {
-       case HCI_AUTO_CONN_DISABLED:
-       case HCI_AUTO_CONN_LINK_LOSS:
-               hci_update_background_scan(hdev);
-               break;
-       case HCI_AUTO_CONN_REPORT:
-               list_add(&params->action, &hdev->pend_le_reports);
-               hci_update_background_scan(hdev);
-               break;
-       case HCI_AUTO_CONN_DIRECT:
-       case HCI_AUTO_CONN_ALWAYS:
-               if (!is_connected(hdev, addr, addr_type)) {
-                       list_add(&params->action, &hdev->pend_le_conns);
-                       hci_update_background_scan(hdev);
-               }
-               break;
-       }
-
-       params->auto_connect = auto_connect;
-
-       BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type,
-              auto_connect);
-
-       return 0;
-}
-
 static void hci_conn_params_free(struct hci_conn_params *params)
 {
        if (params->conn) {
@@ -3901,112 +2844,6 @@ static void le_scan_disable_work(struct work_struct *work)
                BT_ERR("Disable LE scanning request failed: err %d", err);
 }
 
-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.
-        */
-       if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
-           !bacmp(&hdev->bdaddr, 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;
-}
-
 /* Copy the Identity Address of the controller.
  *
  * If the controller has a public BD_ADDR, then by default use that one.
@@ -4015,12 +2852,18 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy,
  *
  * For debugging purposes it is possible to force controllers with a
  * public address to use the static random address instead.
+ *
+ * In case BR/EDR has been disabled on a dual-mode controller and
+ * userspace has configured a static address, then that address
+ * becomes the identity address instead of the public BR/EDR address.
  */
 void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr,
                               u8 *bdaddr_type)
 {
        if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ||
-           !bacmp(&hdev->bdaddr, BDADDR_ANY)) {
+           !bacmp(&hdev->bdaddr, BDADDR_ANY) ||
+           (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags) &&
+            bacmp(&hdev->static_addr, BDADDR_ANY))) {
                bacpy(bdaddr, &hdev->static_addr);
                *bdaddr_type = ADDR_LE_DEV_RANDOM;
        } else {
@@ -4059,6 +2902,12 @@ struct hci_dev *hci_alloc_dev(void)
        hdev->le_conn_max_interval = 0x0038;
        hdev->le_conn_latency = 0x0000;
        hdev->le_supv_timeout = 0x002a;
+       hdev->le_def_tx_len = 0x001b;
+       hdev->le_def_tx_time = 0x0148;
+       hdev->le_max_tx_len = 0x001b;
+       hdev->le_max_tx_time = 0x0148;
+       hdev->le_max_rx_len = 0x001b;
+       hdev->le_max_rx_time = 0x0148;
 
        hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT;
        hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT;
@@ -4539,76 +3388,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
        }
 }
 
-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;
-}
-
 bool hci_req_pending(struct hci_dev *hdev)
 {
        return (hdev->req_status == HCI_REQ_PEND);
 }
 
-static 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;
-}
-
 /* Send HCI command */
 int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
                 const void *param)
@@ -4634,43 +3418,6 @@ int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
        return 0;
 }
 
-/* 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);
-}
-
 /* Get data from the previously sent command */
 void *hci_sent_cmd_data(struct hci_dev *hdev, __u16 opcode)
 {
@@ -5518,302 +4265,3 @@ static void hci_cmd_work(struct work_struct *work)
                }
        }
 }
-
-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, &params->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,
-                                          &params->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, &params->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,
-                                          &params->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, &params->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(&param_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),
-                   &param_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 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);
-}
-
-/* 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_dev *hdev)
-{
-       struct hci_request req;
-       struct hci_conn *conn;
-       int err;
-
-       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);
-
-       hci_req_init(&req, 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);
-       }
-
-       err = hci_req_run(&req, update_background_scan_complete);
-       if (err)
-               BT_ERR("Failed to run HCI request: err %d", err);
-}
-
-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_dev *hdev, struct hci_request *req)
-{
-       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;
-
-       if (req)
-               hci_req_add(req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-       else
-               hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan);
-}
diff --git a/net/bluetooth/hci_debugfs.c b/net/bluetooth/hci_debugfs.c
new file mode 100644 (file)
index 0000000..ee33ce8
--- /dev/null
@@ -0,0 +1,1076 @@
+/*
+   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 <linux/debugfs.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#include "hci_debugfs.h"
+
+static int features_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       u8 p;
+
+       hci_dev_lock(hdev);
+       for (p = 0; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) {
+               seq_printf(f, "%2u: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n", p,
+                          hdev->features[p][0], hdev->features[p][1],
+                          hdev->features[p][2], hdev->features[p][3],
+                          hdev->features[p][4], hdev->features[p][5],
+                          hdev->features[p][6], hdev->features[p][7]);
+       }
+       if (lmp_le_capable(hdev))
+               seq_printf(f, "LE: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
+                          "0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+                          hdev->le_features[0], hdev->le_features[1],
+                          hdev->le_features[2], hdev->le_features[3],
+                          hdev->le_features[4], hdev->le_features[5],
+                          hdev->le_features[6], hdev->le_features[7]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int features_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, features_show, inode->i_private);
+}
+
+static const struct file_operations features_fops = {
+       .open           = features_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int device_list_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct hci_conn_params *p;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->whitelist, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       list_for_each_entry(p, &hdev->le_conn_params, list) {
+               seq_printf(f, "%pMR (type %u) %u\n", &p->addr, p->addr_type,
+                          p->auto_connect);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int device_list_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, device_list_show, inode->i_private);
+}
+
+static const struct file_operations device_list_fops = {
+       .open           = device_list_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int blacklist_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->blacklist, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int blacklist_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blacklist_show, inode->i_private);
+}
+
+static const struct file_operations blacklist_fops = {
+       .open           = blacklist_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int uuids_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct bt_uuid *uuid;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(uuid, &hdev->uuids, list) {
+               u8 i, val[16];
+
+               /* The Bluetooth UUID values are stored in big endian,
+                * but with reversed byte order. So convert them into
+                * the right order for the %pUb modifier.
+                */
+               for (i = 0; i < 16; i++)
+                       val[i] = uuid->uuid[15 - i];
+
+               seq_printf(f, "%pUb\n", val);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int uuids_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, uuids_show, inode->i_private);
+}
+
+static const struct file_operations uuids_fops = {
+       .open           = uuids_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int conn_info_min_age_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val > hdev->conn_info_max_age)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->conn_info_min_age = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_info_min_age_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->conn_info_min_age;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_info_min_age_fops, conn_info_min_age_get,
+                       conn_info_min_age_set, "%llu\n");
+
+static int conn_info_max_age_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val < hdev->conn_info_min_age)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->conn_info_max_age = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_info_max_age_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->conn_info_max_age;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
+                       conn_info_max_age_set, "%llu\n");
+
+void hci_debugfs_create_common(struct hci_dev *hdev)
+{
+       debugfs_create_file("features", 0444, hdev->debugfs, hdev,
+                           &features_fops);
+       debugfs_create_u16("manufacturer", 0444, hdev->debugfs,
+                          &hdev->manufacturer);
+       debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
+       debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+       debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
+                           &device_list_fops);
+       debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
+                           &blacklist_fops);
+       debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+
+       debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
+                           &conn_info_min_age_fops);
+       debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
+                           &conn_info_max_age_fops);
+}
+
+static int inquiry_cache_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       struct discovery_state *cache = &hdev->discovery;
+       struct inquiry_entry *e;
+
+       hci_dev_lock(hdev);
+
+       list_for_each_entry(e, &cache->all, all) {
+               struct inquiry_data *data = &e->data;
+               seq_printf(f, "%pMR %d %d %d 0x%.2x%.2x%.2x 0x%.4x %d %d %u\n",
+                          &data->bdaddr,
+                          data->pscan_rep_mode, data->pscan_period_mode,
+                          data->pscan_mode, data->dev_class[2],
+                          data->dev_class[1], data->dev_class[0],
+                          __le16_to_cpu(data->clock_offset),
+                          data->rssi, data->ssp_mode, e->timestamp);
+       }
+
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int inquiry_cache_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, inquiry_cache_show, inode->i_private);
+}
+
+static const struct file_operations inquiry_cache_fops = {
+       .open           = inquiry_cache_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int link_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct link_key *key;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(key, &hdev->link_keys, list)
+               seq_printf(f, "%pMR %u %*phN %u\n", &key->bdaddr, key->type,
+                          HCI_LINK_KEY_SIZE, key->val, key->pin_len);
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int link_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, link_keys_show, inode->i_private);
+}
+
+static const struct file_operations link_keys_fops = {
+       .open           = link_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int dev_class_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "0x%.2x%.2x%.2x\n", hdev->dev_class[2],
+                  hdev->dev_class[1], hdev->dev_class[0]);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int dev_class_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, dev_class_show, inode->i_private);
+}
+
+static const struct file_operations dev_class_fops = {
+       .open           = dev_class_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int voice_setting_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->voice_setting;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
+                       NULL, "0x%4.4llx\n");
+
+static int auto_accept_delay_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       hdev->auto_accept_delay = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int auto_accept_delay_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->auto_accept_delay;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
+                       auto_accept_delay_set, "%llu\n");
+
+static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations sc_only_mode_fops = {
+       .open           = simple_open,
+       .read           = sc_only_mode_read,
+       .llseek         = default_llseek,
+};
+
+static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
+                                    size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_sc_support_write(struct file *file,
+                                     const char __user *user_buf,
+                                     size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+
+       if (test_bit(HCI_UP, &hdev->flags))
+               return -EBUSY;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
+               return -EALREADY;
+
+       change_bit(HCI_FORCE_SC, &hdev->dbg_flags);
+
+       return count;
+}
+
+static const struct file_operations force_sc_support_fops = {
+       .open           = simple_open,
+       .read           = force_sc_support_read,
+       .write          = force_sc_support_write,
+       .llseek         = default_llseek,
+};
+
+static ssize_t force_lesc_support_read(struct file *file,
+                                      char __user *user_buf,
+                                      size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_lesc_support_write(struct file *file,
+                                       const char __user *user_buf,
+                                       size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
+               return -EALREADY;
+
+       change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
+
+       return count;
+}
+
+static const struct file_operations force_lesc_support_fops = {
+       .open           = simple_open,
+       .read           = force_lesc_support_read,
+       .write          = force_lesc_support_write,
+       .llseek         = default_llseek,
+};
+
+static int idle_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val != 0 && (val < 500 || val > 3600000))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->idle_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int idle_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->idle_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+                       idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+                       sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+                       sniff_max_interval_set, "%llu\n");
+
+void hci_debugfs_create_bredr(struct hci_dev *hdev)
+{
+       debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev,
+                           &inquiry_cache_fops);
+       debugfs_create_file("link_keys", 0400, hdev->debugfs, hdev,
+                           &link_keys_fops);
+       debugfs_create_file("dev_class", 0444, hdev->debugfs, hdev,
+                           &dev_class_fops);
+       debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
+                           &voice_setting_fops);
+
+       if (lmp_ssp_capable(hdev)) {
+               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+                                   hdev, &auto_accept_delay_fops);
+               debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
+                                   hdev, &sc_only_mode_fops);
+
+               debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
+                                   hdev, &force_sc_support_fops);
+
+               if (lmp_le_capable(hdev))
+                       debugfs_create_file("force_lesc_support", 0644,
+                                           hdev->debugfs, hdev,
+                                           &force_lesc_support_fops);
+       }
+
+       if (lmp_sniff_capable(hdev)) {
+               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+                                   hdev, &idle_timeout_fops);
+               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_min_interval_fops);
+               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_max_interval_fops);
+       }
+}
+
+static int identity_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       bdaddr_t addr;
+       u8 addr_type;
+
+       hci_dev_lock(hdev);
+
+       hci_copy_identity_address(hdev, &addr, &addr_type);
+
+       seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
+                  16, hdev->irk, &hdev->rpa);
+
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int identity_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, identity_show, inode->i_private);
+}
+
+static const struct file_operations identity_fops = {
+       .open           = identity_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int rpa_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       /* Require the RPA timeout to be at least 30 seconds and at most
+        * 24 hours.
+        */
+       if (val < 30 || val > (60 * 60 * 24))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->rpa_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int rpa_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->rpa_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
+                       rpa_timeout_set, "%llu\n");
+
+static int random_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->random_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int random_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, random_address_show, inode->i_private);
+}
+
+static const struct file_operations random_address_fops = {
+       .open           = random_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->static_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+       .open           = static_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static ssize_t force_static_address_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t force_static_address_write(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+
+       if (test_bit(HCI_UP, &hdev->flags))
+               return -EBUSY;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags))
+               return -EALREADY;
+
+       change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags);
+
+       return count;
+}
+
+static const struct file_operations force_static_address_fops = {
+       .open           = simple_open,
+       .read           = force_static_address_read,
+       .write          = force_static_address_write,
+       .llseek         = default_llseek,
+};
+
+static int white_list_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->le_white_list, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int white_list_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, white_list_show, inode->i_private);
+}
+
+static const struct file_operations white_list_fops = {
+       .open           = white_list_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct smp_irk *irk;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+               seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
+                          &irk->bdaddr, irk->addr_type,
+                          16, irk->val, &irk->rpa);
+       }
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int identity_resolving_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, identity_resolving_keys_show,
+                          inode->i_private);
+}
+
+static const struct file_operations identity_resolving_keys_fops = {
+       .open           = identity_resolving_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct smp_ltk *ltk;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
+               seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
+                          &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+                          ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+                          __le64_to_cpu(ltk->rand), 16, ltk->val);
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+       .open           = long_term_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+                       conn_min_interval_set, "%llu\n");
+
+static int conn_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+                       conn_max_interval_set, "%llu\n");
+
+static int conn_latency_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val > 0x01f3)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_latency = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_latency_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_latency;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
+                       conn_latency_set, "%llu\n");
+
+static int supervision_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x000a || val > 0x0c80)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_supv_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int supervision_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_supv_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
+                       supervision_timeout_set, "%llu\n");
+
+static int adv_channel_map_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x01 || val > 0x07)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_channel_map = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_channel_map_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_channel_map;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
+                       adv_channel_map_set, "%llu\n");
+
+static int adv_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get,
+                       adv_min_interval_set, "%llu\n");
+
+static int adv_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
+                       adv_max_interval_set, "%llu\n");
+
+void hci_debugfs_create_le(struct hci_dev *hdev)
+{
+       debugfs_create_file("identity", 0400, hdev->debugfs, hdev,
+                           &identity_fops);
+       debugfs_create_file("rpa_timeout", 0644, hdev->debugfs, hdev,
+                           &rpa_timeout_fops);
+       debugfs_create_file("random_address", 0444, hdev->debugfs, hdev,
+                           &random_address_fops);
+       debugfs_create_file("static_address", 0444, hdev->debugfs, hdev,
+                           &static_address_fops);
+
+       /* For controllers with a public address, provide a debug
+        * option to force the usage of the configured static
+        * address. By default the public address is used.
+        */
+       if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+               debugfs_create_file("force_static_address", 0644,
+                                   hdev->debugfs, hdev,
+                                   &force_static_address_fops);
+
+       debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+                         &hdev->le_white_list_size);
+       debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
+                           &white_list_fops);
+       debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs,
+                           hdev, &identity_resolving_keys_fops);
+       debugfs_create_file("long_term_keys", 0400, hdev->debugfs, hdev,
+                           &long_term_keys_fops);
+       debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, hdev,
+                           &conn_min_interval_fops);
+       debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev,
+                           &conn_max_interval_fops);
+       debugfs_create_file("conn_latency", 0644, hdev->debugfs, hdev,
+                           &conn_latency_fops);
+       debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, hdev,
+                           &supervision_timeout_fops);
+       debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev,
+                           &adv_channel_map_fops);
+       debugfs_create_file("adv_min_interval", 0644, hdev->debugfs, hdev,
+                           &adv_min_interval_fops);
+       debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, hdev,
+                           &adv_max_interval_fops);
+       debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs,
+                          &hdev->discov_interleaved_timeout);
+}
+
+void hci_debugfs_create_conn(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       char name[6];
+
+       if (IS_ERR_OR_NULL(hdev->debugfs))
+               return;
+
+       snprintf(name, sizeof(name), "%u", conn->handle);
+       conn->debugfs = debugfs_create_dir(name, hdev->debugfs);
+}
diff --git a/net/bluetooth/hci_debugfs.h b/net/bluetooth/hci_debugfs.h
new file mode 100644 (file)
index 0000000..fb68efe
--- /dev/null
@@ -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);
index 3f2e8b830cbd1cf37b65431bfd76981bc2447dff..0881efd0ad2d8802ea32fd7cd6c882030fc7cafc 100644 (file)
@@ -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);
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
new file mode 100644 (file)
index 0000000..324c641
--- /dev/null
@@ -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, &params->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,
+                                          &params->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, &params->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,
+                                          &params->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, &params->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(&param_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),
+                   &param_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);
+}
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
new file mode 100644 (file)
index 0000000..adf074d
--- /dev/null
@@ -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);
index 693ce8bcd06e6eaeee9fbbdffa375c67a93487f4..3d2f7ad1e65534341ce76608bbf9602b9307c987 100644 (file)
@@ -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(&params->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(&params->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(&params->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(&params->action);
                list_del(&params->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);
index 73f8c75abe6ec40d5376bdeb5114ab99f2e737f1..4fea24275b17a5b3ab4fa614c3982d8cdaed8fee 100644 (file)
@@ -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));
diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c
new file mode 100644 (file)
index 0000000..9c67315
--- /dev/null
@@ -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
diff --git a/net/bluetooth/selftest.h b/net/bluetooth/selftest.h
new file mode 100644 (file)
index 0000000..2aa0a34
--- /dev/null
@@ -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
index b67749bb55bffa511fd180ef52b2e9c271fa35a1..358264c0e78541e15f7ae2339b116a6161eb35a0 100644 (file)
@@ -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
index 3296bf42ae80d627c478c5199e167429ca16079b..60c5b73fcb4b26e3aab32604b58809ca30a4e6f4 100644 (file)
@@ -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 */
index cd919493c976000e6b5cd206014110500e68886e..3c902e9516fb69dc94508ac1f77cda79d4095d16 100644 (file)
@@ -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]);
index 8896477446977a7b4fdab732aec1308c60a46d8b..a25b9bbd077be000d2e1de99bf795ac8e1fc0897 100644 (file)
@@ -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,
index aff54fbd9264e92fd03578d6ecb5298d53226a42..7c46732fad2bdd3f6778fce066cb89330b5aeaa3 100644 (file)
@@ -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)
index 1613b9c65dfa15aafd154042e60abc44517bc9d6..dff55c2d87f34fb8304f7b19d321bc3a08813ee7 100644 (file)
@@ -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,
index c035708ada160d5364417db8a4c3179819016d49..7d31da503dcfcd24d00826d24b121228075ced35 100644 (file)
@@ -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,
index f21e864613d09b6d02c4ffe8f2b7e72e78b75e16..98180a9fff4adc565f7ac9fe7fe711ef549396da 100644 (file)
@@ -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)
index 9ae893057dd761e4e82f563b9f24b4c59e6254bc..6fb6bdf9868c7684b63635a23c2a8b45ae07e4d8 100644 (file)
@@ -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;
index 6aacb181688961178bab9849c23425a7a3e25478..bdccb4ecd30fed81fec03a00c1a3b6452024fcbb 100644 (file)
@@ -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, &params->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 */