Bluetooth: Add support for Add/Remove Device management commands

This allows adding or removing devices from the background scanning
list the kernel maintains. Device flagged for auto-connection will
be automatically connected if they are found.

The passive scanning required for auto-connection will be started
and stopped on demand.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
This commit is contained in:
Marcel Holtmann 2014-06-29 19:44:03 +02:00
parent 037fc415bc
commit 2faade53e6
2 changed files with 111 additions and 1 deletions

View file

@ -436,6 +436,19 @@ struct mgmt_rp_get_clock_info {
__le16 accuracy;
} __packed;
#define MGMT_OP_ADD_DEVICE 0x0033
struct mgmt_cp_add_device {
struct mgmt_addr_info addr;
__u8 action;
} __packed;
#define MGMT_ADD_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1)
#define MGMT_OP_REMOVE_DEVICE 0x0034
struct mgmt_cp_remove_device {
struct mgmt_addr_info addr;
} __packed;
#define MGMT_REMOVE_DEVICE_SIZE MGMT_ADDR_INFO_SIZE
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;

View file

@ -86,6 +86,8 @@ static const u16 mgmt_commands[] = {
MGMT_OP_LOAD_IRKS,
MGMT_OP_GET_CONN_INFO,
MGMT_OP_GET_CLOCK_INFO,
MGMT_OP_ADD_DEVICE,
MGMT_OP_REMOVE_DEVICE,
};
static const u16 mgmt_events[] = {
@ -4966,6 +4968,100 @@ static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data,
return err;
}
static int add_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_add_device *cp = data;
u8 auto_conn, addr_type;
int err;
BT_DBG("%s", hdev->name);
if (!bdaddr_type_is_le(cp->addr.type) ||
!bacmp(&cp->addr.bdaddr, BDADDR_ANY))
return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
if (cp->action != 0x00 && cp->action != 0x01)
return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
hci_dev_lock(hdev);
if (cp->addr.type == BDADDR_LE_PUBLIC)
addr_type = ADDR_LE_DEV_PUBLIC;
else
addr_type = ADDR_LE_DEV_RANDOM;
if (cp->action)
auto_conn = HCI_AUTO_CONN_ALWAYS;
else
auto_conn = HCI_AUTO_CONN_DISABLED;
if (hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type, auto_conn,
hdev->le_conn_min_interval,
hdev->le_conn_max_interval) < 0) {
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_FAILED,
&cp->addr, sizeof(cp->addr));
goto unlock;
}
err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
return err;
}
static int remove_device(struct sock *sk, struct hci_dev *hdev,
void *data, u16 len)
{
struct mgmt_cp_remove_device *cp = data;
int err;
BT_DBG("%s", hdev->name);
hci_dev_lock(hdev);
if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) {
u8 addr_type;
if (!bdaddr_type_is_le(cp->addr.type)) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
goto unlock;
}
if (cp->addr.type == BDADDR_LE_PUBLIC)
addr_type = ADDR_LE_DEV_PUBLIC;
else
addr_type = ADDR_LE_DEV_RANDOM;
hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type);
} else {
if (cp->addr.type) {
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_INVALID_PARAMS,
&cp->addr, sizeof(cp->addr));
goto unlock;
}
hci_conn_params_clear(hdev);
}
err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE,
MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr));
unlock:
hci_dev_unlock(hdev);
return err;
}
static const struct mgmt_handler {
int (*func) (struct sock *sk, struct hci_dev *hdev, void *data,
u16 data_len);
@ -5023,9 +5119,10 @@ static const struct mgmt_handler {
{ load_irks, true, MGMT_LOAD_IRKS_SIZE },
{ get_conn_info, false, MGMT_GET_CONN_INFO_SIZE },
{ get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE },
{ add_device, false, MGMT_ADD_DEVICE_SIZE },
{ remove_device, false, MGMT_REMOVE_DEVICE_SIZE },
};
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
void *buf;