mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-13 23:34:05 +08:00
Bluetooth: hci_sync: Add hci_le_create_conn_sync
This adds hci_le_create_conn_sync and make hci_le_connect use it instead of queueing multiple commands which may conflict with the likes of hci_update_passive_scan which uses hci_cmd_sync_queue. Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com> Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
This commit is contained in:
parent
fee645033e
commit
8e8b92ee60
@ -1121,8 +1121,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
|
|||||||
enum conn_reasons conn_reason);
|
enum conn_reasons conn_reason);
|
||||||
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
u8 dst_type, bool dst_resolved, u8 sec_level,
|
u8 dst_type, bool dst_resolved, u8 sec_level,
|
||||||
u16 conn_timeout, u8 role,
|
u16 conn_timeout, u8 role);
|
||||||
bdaddr_t *direct_rpa);
|
|
||||||
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
u8 sec_level, u8 auth_type,
|
u8 sec_level, u8 auth_type,
|
||||||
enum conn_reasons conn_reason);
|
enum conn_reasons conn_reason);
|
||||||
|
@ -102,3 +102,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev);
|
|||||||
|
|
||||||
int hci_suspend_sync(struct hci_dev *hdev);
|
int hci_suspend_sync(struct hci_dev *hdev);
|
||||||
int hci_resume_sync(struct hci_dev *hdev);
|
int hci_resume_sync(struct hci_dev *hdev);
|
||||||
|
|
||||||
|
struct hci_conn;
|
||||||
|
|
||||||
|
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn);
|
||||||
|
@ -911,267 +911,45 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
|
|||||||
hci_enable_advertising(hdev);
|
hci_enable_advertising(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void create_le_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
|
static void create_le_conn_complete(struct hci_dev *hdev, void *data, int err)
|
||||||
{
|
{
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn = data;
|
||||||
|
|
||||||
hci_dev_lock(hdev);
|
hci_dev_lock(hdev);
|
||||||
|
|
||||||
conn = hci_lookup_le_connect(hdev);
|
if (!err) {
|
||||||
|
|
||||||
if (hdev->adv_instance_cnt)
|
|
||||||
hci_req_resume_adv_instances(hdev);
|
|
||||||
|
|
||||||
if (!status) {
|
|
||||||
hci_connect_le_scan_cleanup(conn);
|
hci_connect_le_scan_cleanup(conn);
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
bt_dev_err(hdev, "request failed to create LE connection: "
|
bt_dev_err(hdev, "request failed to create LE connection: err %d", err);
|
||||||
"status 0x%2.2x", status);
|
|
||||||
|
|
||||||
if (!conn)
|
if (!conn)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
hci_le_conn_failed(conn, status);
|
hci_le_conn_failed(conn, err);
|
||||||
|
|
||||||
done:
|
done:
|
||||||
hci_dev_unlock(hdev);
|
hci_dev_unlock(hdev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool conn_use_rpa(struct hci_conn *conn)
|
static int hci_connect_le_sync(struct hci_dev *hdev, void *data)
|
||||||
{
|
{
|
||||||
struct hci_dev *hdev = conn->hdev;
|
struct hci_conn *conn = data;
|
||||||
|
|
||||||
return hci_dev_test_flag(hdev, HCI_PRIVACY);
|
bt_dev_dbg(hdev, "conn %p", conn);
|
||||||
}
|
|
||||||
|
|
||||||
static void set_ext_conn_params(struct hci_conn *conn,
|
return hci_le_create_conn_sync(hdev, conn);
|
||||||
struct hci_cp_le_ext_conn_param *p)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = conn->hdev;
|
|
||||||
|
|
||||||
memset(p, 0, sizeof(*p));
|
|
||||||
|
|
||||||
p->scan_interval = cpu_to_le16(hdev->le_scan_int_connect);
|
|
||||||
p->scan_window = cpu_to_le16(hdev->le_scan_window_connect);
|
|
||||||
p->conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
|
|
||||||
p->conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
|
|
||||||
p->conn_latency = cpu_to_le16(conn->le_conn_latency);
|
|
||||||
p->supervision_timeout = cpu_to_le16(conn->le_supv_timeout);
|
|
||||||
p->min_ce_len = cpu_to_le16(0x0000);
|
|
||||||
p->max_ce_len = cpu_to_le16(0x0000);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hci_req_add_le_create_conn(struct hci_request *req,
|
|
||||||
struct hci_conn *conn,
|
|
||||||
bdaddr_t *direct_rpa)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = conn->hdev;
|
|
||||||
u8 own_addr_type;
|
|
||||||
|
|
||||||
/* If direct address was provided we use it instead of current
|
|
||||||
* address.
|
|
||||||
*/
|
|
||||||
if (direct_rpa) {
|
|
||||||
if (bacmp(&req->hdev->random_addr, direct_rpa))
|
|
||||||
hci_req_add(req, HCI_OP_LE_SET_RANDOM_ADDR, 6,
|
|
||||||
direct_rpa);
|
|
||||||
|
|
||||||
/* direct address is always RPA */
|
|
||||||
own_addr_type = ADDR_LE_DEV_RANDOM;
|
|
||||||
} else {
|
|
||||||
/* Update random address, but set require_privacy to false so
|
|
||||||
* that we never connect with an non-resolvable address.
|
|
||||||
*/
|
|
||||||
if (hci_update_random_address(req, false, conn_use_rpa(conn),
|
|
||||||
&own_addr_type))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (use_ext_conn(hdev)) {
|
|
||||||
struct hci_cp_le_ext_create_conn *cp;
|
|
||||||
struct hci_cp_le_ext_conn_param *p;
|
|
||||||
u8 data[sizeof(*cp) + sizeof(*p) * 3];
|
|
||||||
u32 plen;
|
|
||||||
|
|
||||||
cp = (void *) data;
|
|
||||||
p = (void *) cp->data;
|
|
||||||
|
|
||||||
memset(cp, 0, sizeof(*cp));
|
|
||||||
|
|
||||||
bacpy(&cp->peer_addr, &conn->dst);
|
|
||||||
cp->peer_addr_type = conn->dst_type;
|
|
||||||
cp->own_addr_type = own_addr_type;
|
|
||||||
|
|
||||||
plen = sizeof(*cp);
|
|
||||||
|
|
||||||
if (scan_1m(hdev)) {
|
|
||||||
cp->phys |= LE_SCAN_PHY_1M;
|
|
||||||
set_ext_conn_params(conn, p);
|
|
||||||
|
|
||||||
p++;
|
|
||||||
plen += sizeof(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scan_2m(hdev)) {
|
|
||||||
cp->phys |= LE_SCAN_PHY_2M;
|
|
||||||
set_ext_conn_params(conn, p);
|
|
||||||
|
|
||||||
p++;
|
|
||||||
plen += sizeof(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scan_coded(hdev)) {
|
|
||||||
cp->phys |= LE_SCAN_PHY_CODED;
|
|
||||||
set_ext_conn_params(conn, p);
|
|
||||||
|
|
||||||
plen += sizeof(*p);
|
|
||||||
}
|
|
||||||
|
|
||||||
hci_req_add(req, HCI_OP_LE_EXT_CREATE_CONN, plen, data);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
struct hci_cp_le_create_conn cp;
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
|
|
||||||
cp.scan_interval = cpu_to_le16(hdev->le_scan_int_connect);
|
|
||||||
cp.scan_window = cpu_to_le16(hdev->le_scan_window_connect);
|
|
||||||
|
|
||||||
bacpy(&cp.peer_addr, &conn->dst);
|
|
||||||
cp.peer_addr_type = conn->dst_type;
|
|
||||||
cp.own_address_type = own_addr_type;
|
|
||||||
cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
|
|
||||||
cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
|
|
||||||
cp.conn_latency = cpu_to_le16(conn->le_conn_latency);
|
|
||||||
cp.supervision_timeout = cpu_to_le16(conn->le_supv_timeout);
|
|
||||||
cp.min_ce_len = cpu_to_le16(0x0000);
|
|
||||||
cp.max_ce_len = cpu_to_le16(0x0000);
|
|
||||||
|
|
||||||
hci_req_add(req, HCI_OP_LE_CREATE_CONN, sizeof(cp), &cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn->state = BT_CONNECT;
|
|
||||||
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void hci_req_directed_advertising(struct hci_request *req,
|
|
||||||
struct hci_conn *conn)
|
|
||||||
{
|
|
||||||
struct hci_dev *hdev = req->hdev;
|
|
||||||
u8 own_addr_type;
|
|
||||||
u8 enable;
|
|
||||||
|
|
||||||
if (ext_adv_capable(hdev)) {
|
|
||||||
struct hci_cp_le_set_ext_adv_params cp;
|
|
||||||
bdaddr_t random_addr;
|
|
||||||
|
|
||||||
/* Set require_privacy to false so that the remote device has a
|
|
||||||
* chance of identifying us.
|
|
||||||
*/
|
|
||||||
if (hci_get_random_address(hdev, false, conn_use_rpa(conn), NULL,
|
|
||||||
&own_addr_type, &random_addr) < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
|
|
||||||
cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_DIRECT_IND);
|
|
||||||
cp.own_addr_type = own_addr_type;
|
|
||||||
cp.channel_map = hdev->le_adv_channel_map;
|
|
||||||
cp.tx_power = HCI_TX_POWER_INVALID;
|
|
||||||
cp.primary_phy = HCI_ADV_PHY_1M;
|
|
||||||
cp.secondary_phy = HCI_ADV_PHY_1M;
|
|
||||||
cp.handle = 0; /* Use instance 0 for directed adv */
|
|
||||||
cp.own_addr_type = own_addr_type;
|
|
||||||
cp.peer_addr_type = conn->dst_type;
|
|
||||||
bacpy(&cp.peer_addr, &conn->dst);
|
|
||||||
|
|
||||||
/* As per Core Spec 5.2 Vol 2, PART E, Sec 7.8.53, for
|
|
||||||
* advertising_event_property LE_LEGACY_ADV_DIRECT_IND
|
|
||||||
* does not supports advertising data when the advertising set already
|
|
||||||
* contains some, the controller shall return erroc code 'Invalid
|
|
||||||
* HCI Command Parameters(0x12).
|
|
||||||
* So it is required to remove adv set for handle 0x00. since we use
|
|
||||||
* instance 0 for directed adv.
|
|
||||||
*/
|
|
||||||
__hci_req_remove_ext_adv_instance(req, cp.handle);
|
|
||||||
|
|
||||||
hci_req_add(req, HCI_OP_LE_SET_EXT_ADV_PARAMS, sizeof(cp), &cp);
|
|
||||||
|
|
||||||
if (own_addr_type == ADDR_LE_DEV_RANDOM &&
|
|
||||||
bacmp(&random_addr, BDADDR_ANY) &&
|
|
||||||
bacmp(&random_addr, &hdev->random_addr)) {
|
|
||||||
struct hci_cp_le_set_adv_set_rand_addr cp;
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
|
|
||||||
cp.handle = 0;
|
|
||||||
bacpy(&cp.bdaddr, &random_addr);
|
|
||||||
|
|
||||||
hci_req_add(req,
|
|
||||||
HCI_OP_LE_SET_ADV_SET_RAND_ADDR,
|
|
||||||
sizeof(cp), &cp);
|
|
||||||
}
|
|
||||||
|
|
||||||
__hci_req_enable_ext_advertising(req, 0x00);
|
|
||||||
} else {
|
|
||||||
struct hci_cp_le_set_adv_param cp;
|
|
||||||
|
|
||||||
/* Clear the HCI_LE_ADV bit temporarily so that the
|
|
||||||
* hci_update_random_address knows that it's safe to go ahead
|
|
||||||
* and write a new random address. The flag will be set back on
|
|
||||||
* as soon as the SET_ADV_ENABLE HCI command completes.
|
|
||||||
*/
|
|
||||||
hci_dev_clear_flag(hdev, HCI_LE_ADV);
|
|
||||||
|
|
||||||
/* Set require_privacy to false so that the remote device has a
|
|
||||||
* chance of identifying us.
|
|
||||||
*/
|
|
||||||
if (hci_update_random_address(req, false, conn_use_rpa(conn),
|
|
||||||
&own_addr_type) < 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
memset(&cp, 0, sizeof(cp));
|
|
||||||
|
|
||||||
/* Some controllers might reject command if intervals are not
|
|
||||||
* within range for undirected advertising.
|
|
||||||
* BCM20702A0 is known to be affected by this.
|
|
||||||
*/
|
|
||||||
cp.min_interval = cpu_to_le16(0x0020);
|
|
||||||
cp.max_interval = cpu_to_le16(0x0020);
|
|
||||||
|
|
||||||
cp.type = LE_ADV_DIRECT_IND;
|
|
||||||
cp.own_address_type = own_addr_type;
|
|
||||||
cp.direct_addr_type = conn->dst_type;
|
|
||||||
bacpy(&cp.direct_addr, &conn->dst);
|
|
||||||
cp.channel_map = hdev->le_adv_channel_map;
|
|
||||||
|
|
||||||
hci_req_add(req, HCI_OP_LE_SET_ADV_PARAM, sizeof(cp), &cp);
|
|
||||||
|
|
||||||
enable = 0x01;
|
|
||||||
hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable),
|
|
||||||
&enable);
|
|
||||||
}
|
|
||||||
|
|
||||||
conn->state = BT_CONNECT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
||||||
u8 dst_type, bool dst_resolved, u8 sec_level,
|
u8 dst_type, bool dst_resolved, u8 sec_level,
|
||||||
u16 conn_timeout, u8 role, bdaddr_t *direct_rpa)
|
u16 conn_timeout, u8 role)
|
||||||
{
|
{
|
||||||
struct hci_conn_params *params;
|
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
struct smp_irk *irk;
|
struct smp_irk *irk;
|
||||||
struct hci_request req;
|
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
/* This ensures that during disable le_scan address resolution
|
|
||||||
* will not be disabled if it is followed by le_create_conn
|
|
||||||
*/
|
|
||||||
bool rpa_le_conn = true;
|
|
||||||
|
|
||||||
/* Let's make sure that le is enabled.*/
|
/* Let's make sure that le is enabled.*/
|
||||||
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
|
if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
|
||||||
if (lmp_le_capable(hdev))
|
if (lmp_le_capable(hdev))
|
||||||
@ -1230,68 +1008,13 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
|
|||||||
conn->sec_level = BT_SECURITY_LOW;
|
conn->sec_level = BT_SECURITY_LOW;
|
||||||
conn->conn_timeout = conn_timeout;
|
conn->conn_timeout = conn_timeout;
|
||||||
|
|
||||||
hci_req_init(&req, hdev);
|
conn->state = BT_CONNECT;
|
||||||
|
clear_bit(HCI_CONN_SCANNING, &conn->flags);
|
||||||
|
|
||||||
/* Disable advertising if we're active. For central role
|
err = hci_cmd_sync_queue(hdev, hci_connect_le_sync, conn,
|
||||||
* connections most controllers will refuse to connect if
|
create_le_conn_complete);
|
||||||
* advertising is enabled, and for peripheral role connections we
|
|
||||||
* anyway have to disable it in order to start directed
|
|
||||||
* advertising. Any registered advertisements will be
|
|
||||||
* re-enabled after the connection attempt is finished.
|
|
||||||
*/
|
|
||||||
if (hci_dev_test_flag(hdev, HCI_LE_ADV))
|
|
||||||
__hci_req_pause_adv_instances(&req);
|
|
||||||
|
|
||||||
/* If requested to connect as peripheral use directed advertising */
|
|
||||||
if (conn->role == HCI_ROLE_SLAVE) {
|
|
||||||
/* If we're active scanning most controllers are unable
|
|
||||||
* to initiate advertising. Simply reject the attempt.
|
|
||||||
*/
|
|
||||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
|
|
||||||
hdev->le_scan_type == LE_SCAN_ACTIVE) {
|
|
||||||
hci_req_purge(&req);
|
|
||||||
hci_conn_del(conn);
|
|
||||||
return ERR_PTR(-EBUSY);
|
|
||||||
}
|
|
||||||
|
|
||||||
hci_req_directed_advertising(&req, conn);
|
|
||||||
goto create_conn;
|
|
||||||
}
|
|
||||||
|
|
||||||
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
|
||||||
if (params) {
|
|
||||||
conn->le_conn_min_interval = params->conn_min_interval;
|
|
||||||
conn->le_conn_max_interval = params->conn_max_interval;
|
|
||||||
conn->le_conn_latency = params->conn_latency;
|
|
||||||
conn->le_supv_timeout = params->supervision_timeout;
|
|
||||||
} else {
|
|
||||||
conn->le_conn_min_interval = hdev->le_conn_min_interval;
|
|
||||||
conn->le_conn_max_interval = hdev->le_conn_max_interval;
|
|
||||||
conn->le_conn_latency = hdev->le_conn_latency;
|
|
||||||
conn->le_supv_timeout = hdev->le_supv_timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If controller is scanning, we stop it since some controllers are
|
|
||||||
* not able to scan and connect at the same time. Also set the
|
|
||||||
* HCI_LE_SCAN_INTERRUPTED flag so that the command complete
|
|
||||||
* handler for scan disabling knows to set the correct discovery
|
|
||||||
* state.
|
|
||||||
*/
|
|
||||||
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
|
||||||
hci_req_add_le_scan_disable(&req, rpa_le_conn);
|
|
||||||
hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
|
|
||||||
}
|
|
||||||
|
|
||||||
hci_req_add_le_create_conn(&req, conn, direct_rpa);
|
|
||||||
|
|
||||||
create_conn:
|
|
||||||
err = hci_req_run(&req, create_le_conn_complete);
|
|
||||||
if (err) {
|
if (err) {
|
||||||
hci_conn_del(conn);
|
hci_conn_del(conn);
|
||||||
|
|
||||||
if (hdev->adv_instance_cnt)
|
|
||||||
hci_req_resume_adv_instances(hdev);
|
|
||||||
|
|
||||||
return ERR_PTR(err);
|
return ERR_PTR(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5755,7 +5755,7 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, void *data,
|
|||||||
static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
|
static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
|
||||||
bdaddr_t *addr,
|
bdaddr_t *addr,
|
||||||
u8 addr_type, bool addr_resolved,
|
u8 addr_type, bool addr_resolved,
|
||||||
u8 adv_type, bdaddr_t *direct_rpa)
|
u8 adv_type)
|
||||||
{
|
{
|
||||||
struct hci_conn *conn;
|
struct hci_conn *conn;
|
||||||
struct hci_conn_params *params;
|
struct hci_conn_params *params;
|
||||||
@ -5810,7 +5810,7 @@ static struct hci_conn *check_pending_le_conn(struct hci_dev *hdev,
|
|||||||
|
|
||||||
conn = hci_connect_le(hdev, addr, addr_type, addr_resolved,
|
conn = hci_connect_le(hdev, addr, addr_type, addr_resolved,
|
||||||
BT_SECURITY_LOW, hdev->def_le_autoconnect_timeout,
|
BT_SECURITY_LOW, hdev->def_le_autoconnect_timeout,
|
||||||
HCI_ROLE_MASTER, direct_rpa);
|
HCI_ROLE_MASTER);
|
||||||
if (!IS_ERR(conn)) {
|
if (!IS_ERR(conn)) {
|
||||||
/* If HCI_AUTO_CONN_EXPLICIT is set, conn is already owned
|
/* If HCI_AUTO_CONN_EXPLICIT is set, conn is already owned
|
||||||
* by higher layer that tried to connect, if no then
|
* by higher layer that tried to connect, if no then
|
||||||
@ -5933,7 +5933,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr,
|
|||||||
* for advertising reports) and is already verified to be RPA above.
|
* for advertising reports) and is already verified to be RPA above.
|
||||||
*/
|
*/
|
||||||
conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, bdaddr_resolved,
|
conn = check_pending_le_conn(hdev, bdaddr, bdaddr_type, bdaddr_resolved,
|
||||||
type, direct_addr);
|
type);
|
||||||
if (!ext_adv && conn && type == LE_ADV_IND && len <= HCI_MAX_AD_LENGTH) {
|
if (!ext_adv && conn && type == LE_ADV_IND && len <= HCI_MAX_AD_LENGTH) {
|
||||||
/* Store report for later inclusion by
|
/* Store report for later inclusion by
|
||||||
* mgmt_device_connected
|
* mgmt_device_connected
|
||||||
|
@ -818,56 +818,6 @@ static void cancel_adv_timeout(struct hci_dev *hdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* This function requires the caller holds hdev->lock */
|
|
||||||
void __hci_req_pause_adv_instances(struct hci_request *req)
|
|
||||||
{
|
|
||||||
bt_dev_dbg(req->hdev, "Pausing advertising instances");
|
|
||||||
|
|
||||||
/* Call to disable any advertisements active on the controller.
|
|
||||||
* This will succeed even if no advertisements are configured.
|
|
||||||
*/
|
|
||||||
__hci_req_disable_advertising(req);
|
|
||||||
|
|
||||||
/* If we are using software rotation, pause the loop */
|
|
||||||
if (!ext_adv_capable(req->hdev))
|
|
||||||
cancel_adv_timeout(req->hdev);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function requires the caller holds hdev->lock */
|
|
||||||
static void __hci_req_resume_adv_instances(struct hci_request *req)
|
|
||||||
{
|
|
||||||
struct adv_info *adv;
|
|
||||||
|
|
||||||
bt_dev_dbg(req->hdev, "Resuming advertising instances");
|
|
||||||
|
|
||||||
if (ext_adv_capable(req->hdev)) {
|
|
||||||
/* Call for each tracked instance to be re-enabled */
|
|
||||||
list_for_each_entry(adv, &req->hdev->adv_instances, list) {
|
|
||||||
__hci_req_enable_ext_advertising(req,
|
|
||||||
adv->instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/* Schedule for most recent instance to be restarted and begin
|
|
||||||
* the software rotation loop
|
|
||||||
*/
|
|
||||||
__hci_req_schedule_adv_instance(req,
|
|
||||||
req->hdev->cur_adv_instance,
|
|
||||||
true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This function requires the caller holds hdev->lock */
|
|
||||||
int hci_req_resume_adv_instances(struct hci_dev *hdev)
|
|
||||||
{
|
|
||||||
struct hci_request req;
|
|
||||||
|
|
||||||
hci_req_init(&req, hdev);
|
|
||||||
__hci_req_resume_adv_instances(&req);
|
|
||||||
|
|
||||||
return hci_req_run(&req, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool adv_cur_instance_is_scannable(struct hci_dev *hdev)
|
static bool adv_cur_instance_is_scannable(struct hci_dev *hdev)
|
||||||
{
|
{
|
||||||
return hci_adv_instance_is_scannable(hdev, hdev->cur_adv_instance);
|
return hci_adv_instance_is_scannable(hdev, hdev->cur_adv_instance);
|
||||||
|
@ -80,8 +80,6 @@ void hci_req_add_le_passive_scan(struct hci_request *req);
|
|||||||
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next);
|
void hci_req_prepare_suspend(struct hci_dev *hdev, enum suspended_state next);
|
||||||
|
|
||||||
void hci_req_disable_address_resolution(struct hci_dev *hdev);
|
void hci_req_disable_address_resolution(struct hci_dev *hdev);
|
||||||
void __hci_req_pause_adv_instances(struct hci_request *req);
|
|
||||||
int hci_req_resume_adv_instances(struct hci_dev *hdev);
|
|
||||||
void hci_req_reenable_advertising(struct hci_dev *hdev);
|
void hci_req_reenable_advertising(struct hci_dev *hdev);
|
||||||
void __hci_req_enable_advertising(struct hci_request *req);
|
void __hci_req_enable_advertising(struct hci_request *req);
|
||||||
void __hci_req_disable_advertising(struct hci_request *req);
|
void __hci_req_disable_advertising(struct hci_request *req);
|
||||||
|
@ -4999,3 +4999,280 @@ int hci_resume_sync(struct hci_dev *hdev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool conn_use_rpa(struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
|
||||||
|
return hci_dev_test_flag(hdev, HCI_PRIVACY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hci_le_ext_directed_advertising_sync(struct hci_dev *hdev,
|
||||||
|
struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
struct hci_cp_le_set_ext_adv_params cp;
|
||||||
|
int err;
|
||||||
|
bdaddr_t random_addr;
|
||||||
|
u8 own_addr_type;
|
||||||
|
|
||||||
|
err = hci_update_random_address_sync(hdev, false, conn_use_rpa(conn),
|
||||||
|
&own_addr_type);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Set require_privacy to false so that the remote device has a
|
||||||
|
* chance of identifying us.
|
||||||
|
*/
|
||||||
|
err = hci_get_random_address(hdev, false, conn_use_rpa(conn), NULL,
|
||||||
|
&own_addr_type, &random_addr);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
cp.evt_properties = cpu_to_le16(LE_LEGACY_ADV_DIRECT_IND);
|
||||||
|
cp.own_addr_type = own_addr_type;
|
||||||
|
cp.channel_map = hdev->le_adv_channel_map;
|
||||||
|
cp.tx_power = HCI_TX_POWER_INVALID;
|
||||||
|
cp.primary_phy = HCI_ADV_PHY_1M;
|
||||||
|
cp.secondary_phy = HCI_ADV_PHY_1M;
|
||||||
|
cp.handle = 0x00; /* Use instance 0 for directed adv */
|
||||||
|
cp.own_addr_type = own_addr_type;
|
||||||
|
cp.peer_addr_type = conn->dst_type;
|
||||||
|
bacpy(&cp.peer_addr, &conn->dst);
|
||||||
|
|
||||||
|
/* As per Core Spec 5.2 Vol 2, PART E, Sec 7.8.53, for
|
||||||
|
* advertising_event_property LE_LEGACY_ADV_DIRECT_IND
|
||||||
|
* does not supports advertising data when the advertising set already
|
||||||
|
* contains some, the controller shall return erroc code 'Invalid
|
||||||
|
* HCI Command Parameters(0x12).
|
||||||
|
* So it is required to remove adv set for handle 0x00. since we use
|
||||||
|
* instance 0 for directed adv.
|
||||||
|
*/
|
||||||
|
err = hci_remove_ext_adv_instance_sync(hdev, cp.handle, NULL);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_EXT_ADV_PARAMS,
|
||||||
|
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
/* Check if random address need to be updated */
|
||||||
|
if (own_addr_type == ADDR_LE_DEV_RANDOM &&
|
||||||
|
bacmp(&random_addr, BDADDR_ANY) &&
|
||||||
|
bacmp(&random_addr, &hdev->random_addr)) {
|
||||||
|
err = hci_set_adv_set_random_addr_sync(hdev, 0x00,
|
||||||
|
&random_addr);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hci_enable_ext_advertising_sync(hdev, 0x00);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int hci_le_directed_advertising_sync(struct hci_dev *hdev,
|
||||||
|
struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
struct hci_cp_le_set_adv_param cp;
|
||||||
|
u8 status;
|
||||||
|
u8 own_addr_type;
|
||||||
|
u8 enable;
|
||||||
|
|
||||||
|
if (ext_adv_capable(hdev))
|
||||||
|
return hci_le_ext_directed_advertising_sync(hdev, conn);
|
||||||
|
|
||||||
|
/* Clear the HCI_LE_ADV bit temporarily so that the
|
||||||
|
* hci_update_random_address knows that it's safe to go ahead
|
||||||
|
* and write a new random address. The flag will be set back on
|
||||||
|
* as soon as the SET_ADV_ENABLE HCI command completes.
|
||||||
|
*/
|
||||||
|
hci_dev_clear_flag(hdev, HCI_LE_ADV);
|
||||||
|
|
||||||
|
/* Set require_privacy to false so that the remote device has a
|
||||||
|
* chance of identifying us.
|
||||||
|
*/
|
||||||
|
status = hci_update_random_address_sync(hdev, false, conn_use_rpa(conn),
|
||||||
|
&own_addr_type);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
/* Some controllers might reject command if intervals are not
|
||||||
|
* within range for undirected advertising.
|
||||||
|
* BCM20702A0 is known to be affected by this.
|
||||||
|
*/
|
||||||
|
cp.min_interval = cpu_to_le16(0x0020);
|
||||||
|
cp.max_interval = cpu_to_le16(0x0020);
|
||||||
|
|
||||||
|
cp.type = LE_ADV_DIRECT_IND;
|
||||||
|
cp.own_address_type = own_addr_type;
|
||||||
|
cp.direct_addr_type = conn->dst_type;
|
||||||
|
bacpy(&cp.direct_addr, &conn->dst);
|
||||||
|
cp.channel_map = hdev->le_adv_channel_map;
|
||||||
|
|
||||||
|
status = __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_PARAM,
|
||||||
|
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||||
|
if (status)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
enable = 0x01;
|
||||||
|
|
||||||
|
return __hci_cmd_sync_status(hdev, HCI_OP_LE_SET_ADV_ENABLE,
|
||||||
|
sizeof(enable), &enable, HCI_CMD_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_ext_conn_params(struct hci_conn *conn,
|
||||||
|
struct hci_cp_le_ext_conn_param *p)
|
||||||
|
{
|
||||||
|
struct hci_dev *hdev = conn->hdev;
|
||||||
|
|
||||||
|
memset(p, 0, sizeof(*p));
|
||||||
|
|
||||||
|
p->scan_interval = cpu_to_le16(hdev->le_scan_int_connect);
|
||||||
|
p->scan_window = cpu_to_le16(hdev->le_scan_window_connect);
|
||||||
|
p->conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
|
||||||
|
p->conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
|
||||||
|
p->conn_latency = cpu_to_le16(conn->le_conn_latency);
|
||||||
|
p->supervision_timeout = cpu_to_le16(conn->le_supv_timeout);
|
||||||
|
p->min_ce_len = cpu_to_le16(0x0000);
|
||||||
|
p->max_ce_len = cpu_to_le16(0x0000);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_le_ext_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn,
|
||||||
|
u8 own_addr_type)
|
||||||
|
{
|
||||||
|
struct hci_cp_le_ext_create_conn *cp;
|
||||||
|
struct hci_cp_le_ext_conn_param *p;
|
||||||
|
u8 data[sizeof(*cp) + sizeof(*p) * 3];
|
||||||
|
u32 plen;
|
||||||
|
|
||||||
|
cp = (void *)data;
|
||||||
|
p = (void *)cp->data;
|
||||||
|
|
||||||
|
memset(cp, 0, sizeof(*cp));
|
||||||
|
|
||||||
|
bacpy(&cp->peer_addr, &conn->dst);
|
||||||
|
cp->peer_addr_type = conn->dst_type;
|
||||||
|
cp->own_addr_type = own_addr_type;
|
||||||
|
|
||||||
|
plen = sizeof(*cp);
|
||||||
|
|
||||||
|
if (scan_1m(hdev)) {
|
||||||
|
cp->phys |= LE_SCAN_PHY_1M;
|
||||||
|
set_ext_conn_params(conn, p);
|
||||||
|
|
||||||
|
p++;
|
||||||
|
plen += sizeof(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scan_2m(hdev)) {
|
||||||
|
cp->phys |= LE_SCAN_PHY_2M;
|
||||||
|
set_ext_conn_params(conn, p);
|
||||||
|
|
||||||
|
p++;
|
||||||
|
plen += sizeof(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (scan_coded(hdev)) {
|
||||||
|
cp->phys |= LE_SCAN_PHY_CODED;
|
||||||
|
set_ext_conn_params(conn, p);
|
||||||
|
|
||||||
|
plen += sizeof(*p);
|
||||||
|
}
|
||||||
|
|
||||||
|
return __hci_cmd_sync_status(hdev, HCI_OP_LE_EXT_CREATE_CONN,
|
||||||
|
plen, data, HCI_CMD_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
int hci_le_create_conn_sync(struct hci_dev *hdev, struct hci_conn *conn)
|
||||||
|
{
|
||||||
|
struct hci_cp_le_create_conn cp;
|
||||||
|
struct hci_conn_params *params;
|
||||||
|
u8 own_addr_type;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
/* Disable advertising if we're active. For central role
|
||||||
|
* connections most controllers will refuse to connect if
|
||||||
|
* advertising is enabled, and for peripheral role connections we
|
||||||
|
* anyway have to disable it in order to start directed
|
||||||
|
* advertising. Any registered advertisements will be
|
||||||
|
* re-enabled after the connection attempt is finished.
|
||||||
|
*/
|
||||||
|
hci_pause_advertising_sync(hdev);
|
||||||
|
|
||||||
|
/* If requested to connect as peripheral use directed advertising */
|
||||||
|
if (conn->role == HCI_ROLE_SLAVE) {
|
||||||
|
/* If we're active scanning most controllers are unable
|
||||||
|
* to initiate advertising. Simply reject the attempt.
|
||||||
|
*/
|
||||||
|
if (hci_dev_test_flag(hdev, HCI_LE_SCAN) &&
|
||||||
|
hdev->le_scan_type == LE_SCAN_ACTIVE) {
|
||||||
|
hci_conn_del(conn);
|
||||||
|
return -EBUSY;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = hci_le_directed_advertising_sync(hdev, conn);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type);
|
||||||
|
if (params) {
|
||||||
|
conn->le_conn_min_interval = params->conn_min_interval;
|
||||||
|
conn->le_conn_max_interval = params->conn_max_interval;
|
||||||
|
conn->le_conn_latency = params->conn_latency;
|
||||||
|
conn->le_supv_timeout = params->supervision_timeout;
|
||||||
|
} else {
|
||||||
|
conn->le_conn_min_interval = hdev->le_conn_min_interval;
|
||||||
|
conn->le_conn_max_interval = hdev->le_conn_max_interval;
|
||||||
|
conn->le_conn_latency = hdev->le_conn_latency;
|
||||||
|
conn->le_supv_timeout = hdev->le_supv_timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If controller is scanning, we stop it since some controllers are
|
||||||
|
* not able to scan and connect at the same time. Also set the
|
||||||
|
* HCI_LE_SCAN_INTERRUPTED flag so that the command complete
|
||||||
|
* handler for scan disabling knows to set the correct discovery
|
||||||
|
* state.
|
||||||
|
*/
|
||||||
|
if (hci_dev_test_flag(hdev, HCI_LE_SCAN)) {
|
||||||
|
hci_scan_disable_sync(hdev);
|
||||||
|
hci_dev_set_flag(hdev, HCI_LE_SCAN_INTERRUPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update random address, but set require_privacy to false so
|
||||||
|
* that we never connect with an non-resolvable address.
|
||||||
|
*/
|
||||||
|
err = hci_update_random_address_sync(hdev, false, conn_use_rpa(conn),
|
||||||
|
&own_addr_type);
|
||||||
|
if (err)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
if (use_ext_conn(hdev)) {
|
||||||
|
err = hci_le_ext_create_conn_sync(hdev, conn, own_addr_type);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cp, 0, sizeof(cp));
|
||||||
|
|
||||||
|
cp.scan_interval = cpu_to_le16(hdev->le_scan_int_connect);
|
||||||
|
cp.scan_window = cpu_to_le16(hdev->le_scan_window_connect);
|
||||||
|
|
||||||
|
bacpy(&cp.peer_addr, &conn->dst);
|
||||||
|
cp.peer_addr_type = conn->dst_type;
|
||||||
|
cp.own_address_type = own_addr_type;
|
||||||
|
cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval);
|
||||||
|
cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval);
|
||||||
|
cp.conn_latency = cpu_to_le16(conn->le_conn_latency);
|
||||||
|
cp.supervision_timeout = cpu_to_le16(conn->le_supv_timeout);
|
||||||
|
cp.min_ce_len = cpu_to_le16(0x0000);
|
||||||
|
cp.max_ce_len = cpu_to_le16(0x0000);
|
||||||
|
|
||||||
|
err = __hci_cmd_sync_status(hdev, HCI_OP_LE_CREATE_CONN,
|
||||||
|
sizeof(cp), &cp, HCI_CMD_TIMEOUT);
|
||||||
|
|
||||||
|
done:
|
||||||
|
hci_resume_advertising_sync(hdev);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
@ -7905,7 +7905,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
|
|||||||
hcon = hci_connect_le(hdev, dst, dst_type, false,
|
hcon = hci_connect_le(hdev, dst, dst_type, false,
|
||||||
chan->sec_level,
|
chan->sec_level,
|
||||||
HCI_LE_CONN_TIMEOUT,
|
HCI_LE_CONN_TIMEOUT,
|
||||||
HCI_ROLE_SLAVE, NULL);
|
HCI_ROLE_SLAVE);
|
||||||
else
|
else
|
||||||
hcon = hci_connect_le_scan(hdev, dst, dst_type,
|
hcon = hci_connect_le_scan(hdev, dst, dst_type,
|
||||||
chan->sec_level,
|
chan->sec_level,
|
||||||
|
Loading…
Reference in New Issue
Block a user