Commit a3b76bf4 authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz
Browse files

Bluetooth: MGMT: Allow use of Set Device Flags without Add Device



In certain cases setting devices flags like HCI_CONN_FLAG_PAST it
shouldn't require to do Add Device first since it may not need to add
an auto-connect policy, so this instead just automatically creates
a hci_conn_params if one cannot be found using HCI_AUTO_CONN_DISABLED.

Signed-off-by: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
parent f817db10
Loading
Loading
Loading
Loading
+76 −72
Original line number Diff line number Diff line
@@ -5122,6 +5122,69 @@ static void device_flags_changed(struct sock *sk, struct hci_dev *hdev,
	mgmt_event(MGMT_EV_DEVICE_FLAGS_CHANGED, hdev, &ev, sizeof(ev), sk);
}

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 struct hci_conn_params *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 NULL;

	if (params->auto_connect == auto_connect)
		return params;

	hci_pend_le_list_del_init(params);

	switch (auto_connect) {
	case HCI_AUTO_CONN_DISABLED:
	case HCI_AUTO_CONN_LINK_LOSS:
		/* If auto connect is being disabled when we're trying to
		 * connect to device, keep connecting.
		 */
		if (params->explicit_connect)
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
		break;
	case HCI_AUTO_CONN_REPORT:
		if (params->explicit_connect)
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
		else
			hci_pend_le_list_add(params, &hdev->pend_le_reports);
		break;
	case HCI_AUTO_CONN_DIRECT:
	case HCI_AUTO_CONN_ALWAYS:
		if (!is_connected(hdev, addr, addr_type))
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
		break;
	}

	params->auto_connect = auto_connect;

	bt_dev_dbg(hdev, "addr %pMR (type %u) auto_connect %u",
		   addr, addr_type, auto_connect);

	return params;
}

static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
			    u16 len)
{
@@ -5164,11 +5227,18 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,

	params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
					le_addr_type(cp->addr.type));
	if (!params) {
		/* Create a new hci_conn_params if it doesn't exist */
		params = hci_conn_params_set(hdev, &cp->addr.bdaddr,
					     le_addr_type(cp->addr.type),
					     HCI_AUTO_CONN_DISABLED);
		if (!params) {
			bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
			    &cp->addr.bdaddr, le_addr_type(cp->addr.type));
				    &cp->addr.bdaddr,
				    le_addr_type(cp->addr.type));
			goto unlock;
		}
	}

	supported_flags = hdev->conn_flags;

@@ -7554,68 +7624,6 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
	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_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;

	hci_pend_le_list_del_init(params);

	switch (auto_connect) {
	case HCI_AUTO_CONN_DISABLED:
	case HCI_AUTO_CONN_LINK_LOSS:
		/* If auto connect is being disabled when we're trying to
		 * connect to device, keep connecting.
		 */
		if (params->explicit_connect)
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
		break;
	case HCI_AUTO_CONN_REPORT:
		if (params->explicit_connect)
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
		else
			hci_pend_le_list_add(params, &hdev->pend_le_reports);
		break;
	case HCI_AUTO_CONN_DIRECT:
	case HCI_AUTO_CONN_ALWAYS:
		if (!is_connected(hdev, addr, addr_type))
			hci_pend_le_list_add(params, &hdev->pend_le_conns);
		break;
	}

	params->auto_connect = auto_connect;

	bt_dev_dbg(hdev, "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)
{
@@ -7727,17 +7735,13 @@ 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,
				auto_conn) < 0) {
	params = hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type,
				     auto_conn);
	if (!params) {
		err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
					MGMT_STATUS_FAILED, &cp->addr,
					sizeof(cp->addr));
		goto unlock;
	} else {
		params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
						addr_type);
		if (params)
			current_flags = params->flags;
	}

	cmd = mgmt_pending_new(sk, MGMT_OP_ADD_DEVICE, hdev, data, len);