Commit 8e8b92ee authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Marcel Holtmann
Browse files

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: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent fee64503
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -1121,8 +1121,7 @@ struct hci_conn *hci_connect_le_scan(struct hci_dev *hdev, bdaddr_t *dst,
				     enum conn_reasons conn_reason);
struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
				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 *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst,
				 u8 sec_level, u8 auth_type,
				 enum conn_reasons conn_reason);
+4 −0
Original line number Diff line number Diff line
@@ -102,3 +102,7 @@ int hci_stop_discovery_sync(struct hci_dev *hdev);

int hci_suspend_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);
+14 −291
Original line number Diff line number Diff line
@@ -911,267 +911,45 @@ void hci_le_conn_failed(struct hci_conn *conn, u8 status)
	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);

	conn = hci_lookup_le_connect(hdev);

	if (hdev->adv_instance_cnt)
		hci_req_resume_adv_instances(hdev);

	if (!status) {
	if (!err) {
		hci_connect_le_scan_cleanup(conn);
		goto done;
	}

	bt_dev_err(hdev, "request failed to create LE connection: "
		   "status 0x%2.2x", status);
	bt_dev_err(hdev, "request failed to create LE connection: err %d", err);

	if (!conn)
		goto done;

	hci_le_conn_failed(conn, status);
	hci_le_conn_failed(conn, err);

done:
	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,
				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;
	return hci_le_create_conn_sync(hdev, conn);
}

struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst,
				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 smp_irk *irk;
	struct hci_request req;
	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.*/
	if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) {
		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->conn_timeout = conn_timeout;

	hci_req_init(&req, hdev);

	/* 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.
	 */
	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);
	conn->state = BT_CONNECT;
	clear_bit(HCI_CONN_SCANNING, &conn->flags);

create_conn:
	err = hci_req_run(&req, create_le_conn_complete);
	err = hci_cmd_sync_queue(hdev, hci_connect_le_sync, conn,
				 create_le_conn_complete);
	if (err) {
		hci_conn_del(conn);

		if (hdev->adv_instance_cnt)
			hci_req_resume_adv_instances(hdev);

		return ERR_PTR(err);
	}

+3 −3
Original line number Diff line number Diff line
@@ -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,
					      bdaddr_t *addr,
					      u8 addr_type, bool addr_resolved,
					      u8 adv_type, bdaddr_t *direct_rpa)
					      u8 adv_type)
{
	struct hci_conn *conn;
	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,
			      BT_SECURITY_LOW, hdev->def_le_autoconnect_timeout,
			      HCI_ROLE_MASTER, direct_rpa);
			      HCI_ROLE_MASTER);
	if (!IS_ERR(conn)) {
		/* If HCI_AUTO_CONN_EXPLICIT is set, conn is already owned
		 * 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.
	 */
	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) {
		/* Store report for later inclusion by
		 * mgmt_device_connected
+0 −50
Original line number Diff line number Diff line
@@ -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)
{
	return hci_adv_instance_is_scannable(hdev, hdev->cur_adv_instance);
Loading