diff options
Diffstat (limited to 'net/bluetooth/hci_core.c')
-rw-r--r-- | net/bluetooth/hci_core.c | 169 |
1 files changed, 142 insertions, 27 deletions
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 34c17a0645ce..3322d3f4c85a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -609,6 +609,7 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { u8 mode = 0x01; + hci_req_add(req, HCI_OP_WRITE_SSP_MODE, sizeof(mode), &mode); } else { @@ -870,8 +871,10 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_READ_SYNC_TRAIN_PARAMS, 0, NULL); /* Enable Secure Connections if supported and configured */ - if (bredr_sc_enabled(hdev)) { + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags) && + bredr_sc_enabled(hdev)) { u8 support = 0x01; + hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT, sizeof(support), &support); } @@ -1614,6 +1617,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) cancel_delayed_work(&hdev->service_cache); cancel_delayed_work_sync(&hdev->le_scan_disable); + cancel_delayed_work_sync(&hdev->le_scan_restart); if (test_bit(HCI_MGMT, &hdev->dev_flags)) cancel_delayed_work_sync(&hdev->rpa_expired); @@ -1625,6 +1629,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_lock(hdev); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { if (hdev->dev_type == HCI_BREDR) mgmt_powered(hdev, 0); @@ -1635,6 +1641,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_conn_hash_flush(hdev); hci_dev_unlock(hdev); + smp_unregister(hdev); + hci_notify(hdev, HCI_DEV_DOWN); if (hdev->flush) @@ -1714,32 +1722,14 @@ done: return err; } -int hci_dev_reset(__u16 dev) +static int hci_dev_do_reset(struct hci_dev *hdev) { - struct hci_dev *hdev; - int ret = 0; + int ret; - hdev = hci_dev_get(dev); - if (!hdev) - return -ENODEV; + BT_DBG("%s %p", hdev->name, hdev); hci_req_lock(hdev); - if (!test_bit(HCI_UP, &hdev->flags)) { - ret = -ENETDOWN; - goto done; - } - - if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { - ret = -EBUSY; - goto done; - } - - if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { - ret = -EOPNOTSUPP; - goto done; - } - /* Drop queues */ skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->cmd_q); @@ -1762,12 +1752,41 @@ int hci_dev_reset(__u16 dev) ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); -done: hci_req_unlock(hdev); - hci_dev_put(hdev); return ret; } +int hci_dev_reset(__u16 dev) +{ + struct hci_dev *hdev; + int err; + + hdev = hci_dev_get(dev); + if (!hdev) + return -ENODEV; + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = -ENETDOWN; + goto done; + } + + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = -EBUSY; + goto done; + } + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + err = -EOPNOTSUPP; + goto done; + } + + err = hci_dev_do_reset(hdev); + +done: + hci_dev_put(hdev); + return err; +} + int hci_dev_reset_stat(__u16 dev) { struct hci_dev *hdev; @@ -2131,8 +2150,24 @@ static void hci_power_off(struct work_struct *work) BT_DBG("%s", hdev->name); hci_dev_do_close(hdev); +} - smp_unregister(hdev); +static void hci_error_reset(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, error_reset); + + BT_DBG("%s", hdev->name); + + if (hdev->hw_error) + hdev->hw_error(hdev, hdev->hw_error_code); + else + BT_ERR("%s hardware error 0x%2.2x", hdev->name, + hdev->hw_error_code); + + if (hci_dev_do_close(hdev)) + return; + + hci_dev_do_open(hdev); } static void hci_discov_off(struct work_struct *work) @@ -2547,9 +2582,15 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, if (hash192 && rand192) { memcpy(data->hash192, hash192, sizeof(data->hash192)); memcpy(data->rand192, rand192, sizeof(data->rand192)); + if (hash256 && rand256) + data->present = 0x03; } else { memset(data->hash192, 0, sizeof(data->hash192)); memset(data->rand192, 0, sizeof(data->rand192)); + if (hash256 && rand256) + data->present = 0x02; + else + data->present = 0x00; } if (hash256 && rand256) { @@ -2558,6 +2599,8 @@ int hci_add_remote_oob_data(struct hci_dev *hdev, bdaddr_t *bdaddr, } else { memset(data->hash256, 0, sizeof(data->hash256)); memset(data->rand256, 0, sizeof(data->rand256)); + if (hash192 && rand192) + data->present = 0x01; } BT_DBG("%s for %pMR", hdev->name, bdaddr); @@ -2788,6 +2831,8 @@ static void le_scan_disable_work_complete(struct hci_dev *hdev, u8 status, return; } + hdev->discovery.scan_start = 0; + switch (hdev->discovery.type) { case DISCOV_TYPE_LE: hci_dev_lock(hdev); @@ -2827,6 +2872,8 @@ static void le_scan_disable_work(struct work_struct *work) BT_DBG("%s", hdev->name); + cancel_delayed_work_sync(&hdev->le_scan_restart); + hci_req_init(&req, hdev); hci_req_add_le_scan_disable(&req); @@ -2836,6 +2883,74 @@ static void le_scan_disable_work(struct work_struct *work) BT_ERR("Disable LE scanning request failed: err %d", err); } +static void le_scan_restart_work_complete(struct hci_dev *hdev, u8 status, + u16 opcode) +{ + unsigned long timeout, duration, scan_start, now; + + BT_DBG("%s", hdev->name); + + if (status) { + BT_ERR("Failed to restart LE scan: status %d", status); + return; + } + + if (!test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks) || + !hdev->discovery.scan_start) + return; + + /* When the scan was started, hdev->le_scan_disable has been queued + * after duration from scan_start. During scan restart this job + * has been canceled, and we need to queue it again after proper + * timeout, to make sure that scan does not run indefinitely. + */ + duration = hdev->discovery.scan_duration; + scan_start = hdev->discovery.scan_start; + now = jiffies; + if (now - scan_start <= duration) { + int elapsed; + + if (now >= scan_start) + elapsed = now - scan_start; + else + elapsed = ULONG_MAX - scan_start + now; + + timeout = duration - elapsed; + } else { + timeout = 0; + } + queue_delayed_work(hdev->workqueue, + &hdev->le_scan_disable, timeout); +} + +static void le_scan_restart_work(struct work_struct *work) +{ + struct hci_dev *hdev = container_of(work, struct hci_dev, + le_scan_restart.work); + struct hci_request req; + struct hci_cp_le_set_scan_enable cp; + int err; + + BT_DBG("%s", hdev->name); + + /* If controller is not scanning we are done. */ + if (!test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + return; + + hci_req_init(&req, hdev); + + hci_req_add_le_scan_disable(&req); + + memset(&cp, 0, sizeof(cp)); + cp.enable = LE_SCAN_ENABLE; + cp.filter_dup = LE_SCAN_FILTER_DUP_ENABLE; + hci_req_add(&req, HCI_OP_LE_SET_SCAN_ENABLE, sizeof(cp), &cp); + + err = hci_req_run(&req, le_scan_restart_work_complete); + if (err) + BT_ERR("Restart LE scan request failed: err %d", err); +} + /* Copy the Identity Address of the controller. * * If the controller has a public BD_ADDR, then by default use that one. @@ -2927,10 +3042,12 @@ struct hci_dev *hci_alloc_dev(void) INIT_WORK(&hdev->cmd_work, hci_cmd_work); INIT_WORK(&hdev->tx_work, hci_tx_work); INIT_WORK(&hdev->power_on, hci_power_on); + INIT_WORK(&hdev->error_reset, hci_error_reset); INIT_DELAYED_WORK(&hdev->power_off, hci_power_off); INIT_DELAYED_WORK(&hdev->discov_off, hci_discov_off); INIT_DELAYED_WORK(&hdev->le_scan_disable, le_scan_disable_work); + INIT_DELAYED_WORK(&hdev->le_scan_restart, le_scan_restart_work); skb_queue_head_init(&hdev->rx_q); skb_queue_head_init(&hdev->cmd_q); @@ -3100,8 +3217,6 @@ void hci_unregister_dev(struct hci_dev *hdev) rfkill_destroy(hdev->rfkill); } - smp_unregister(hdev); - device_del(&hdev->dev); debugfs_remove_recursive(hdev->debugfs); |