void *user_data;
};
-LIST_HEAD(cmd_list);
+static LIST_HEAD(cmd_list);
static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status)
{
hci_del_off_timer(hdev);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
set_bit(HCI_MGMT, &hdev->flags);
memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name));
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp));
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
up = test_bit(HCI_UP, &hdev->flags);
if ((cp->val && up) || (!cp->val && !up)) {
err = 0;
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
}
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN);
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN);
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (cp->val)
set_bit(HCI_PAIRABLE, &hdev->flags);
err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
uuid = kmalloc(sizeof(*uuid), GFP_ATOMIC);
if (!uuid) {
err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) {
err = hci_uuids_clear(hdev);
err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0);
unlock:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
hdev->major_class = cp->major;
hdev->minor_class = cp->minor;
if (err == 0)
err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0);
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
BT_DBG("hci%u enable %d", index, cp->enable);
err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL,
0);
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
struct hci_dev *hdev;
struct mgmt_cp_load_keys *cp;
u16 key_count, expected_len;
- int i;
+ int i, err;
cp = (void *) data;
key_count = get_unaligned_le16(&cp->key_count);
expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info);
- if (expected_len != len) {
- BT_ERR("load_keys: expected %u bytes, got %u bytes",
- len, expected_len);
+ if (expected_len > len) {
+ BT_ERR("load_keys: expected at least %u bytes, got %u bytes",
+ expected_len, len);
return -EINVAL;
}
BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys,
key_count);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
hci_link_keys_clear(hdev);
else
clear_bit(HCI_DEBUG_KEYS, &hdev->flags);
- for (i = 0; i < key_count; i++) {
- struct mgmt_key_info *key = &cp->keys[i];
+ len -= sizeof(*cp);
+ i = 0;
+
+ while (i < len) {
+ struct mgmt_key_info *key = (void *) cp->keys + i;
+
+ i += sizeof(*key) + key->dlen;
+
+ if (key->type == HCI_LK_SMP_LTK) {
+ struct key_master_id *id = (void *) key->data;
+
+ if (key->dlen != sizeof(struct key_master_id))
+ continue;
+
+ hci_add_ltk(hdev, 0, &key->bdaddr, key->pin_len,
+ id->ediv, id->rand, key->val);
+
+ continue;
+ }
hci_add_link_key(hdev, NULL, 0, &key->bdaddr, key->val, key->type,
key->pin_len);
}
- hci_dev_unlock(hdev);
+ err = cmd_complete(sk, index, MGMT_OP_LOAD_KEYS, NULL, 0);
+
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
- return 0;
+ return err;
}
static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len)
if (!hdev)
return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
err = hci_remove_link_key(hdev, &cp->bdaddr);
if (err < 0) {
put_unaligned_le16(conn->handle, &dc.handle);
dc.reason = 0x13; /* Remote User Terminated Connection */
- err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, 0, NULL);
+ err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc);
}
unlock:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN);
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
count = 0;
list_for_each(p, &hdev->conn_hash.list) {
put_unaligned_le16(count, &rp->conn_count);
- read_lock(&hci_dev_list_lock);
-
i = 0;
list_for_each(p, &hdev->conn_hash.list) {
struct hci_conn *c = list_entry(p, struct hci_conn, list);
bacpy(&rp->conn[i++], &c->dst);
}
- read_unlock(&hci_dev_list_lock);
-
err = cmd_complete(sk, index, MGMT_OP_GET_CONNECTIONS, rp, rp_len);
unlock:
kfree(rp);
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
}
+static int send_pin_code_neg_reply(struct sock *sk, u16 index,
+ struct hci_dev *hdev, struct mgmt_cp_pin_code_neg_reply *cp)
+{
+ struct pending_cmd *cmd;
+ int err;
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index, cp,
+ sizeof(*cp));
+ if (!cmd)
+ return -ENOMEM;
+
+ err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
+ &cp->bdaddr);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+ return err;
+}
+
static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data,
u16 len)
{
struct hci_dev *hdev;
+ struct hci_conn *conn;
struct mgmt_cp_pin_code_reply *cp;
+ struct mgmt_cp_pin_code_neg_reply ncp;
struct hci_cp_pin_code_reply reply;
struct pending_cmd *cmd;
int err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENETDOWN);
goto failed;
}
+ conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr);
+ if (!conn) {
+ err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENOTCONN);
+ goto failed;
+ }
+
+ if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) {
+ bacpy(&ncp.bdaddr, &cp->bdaddr);
+
+ BT_ERR("PIN code is not 16 bytes long");
+
+ err = send_pin_code_neg_reply(sk, index, hdev, &ncp);
+ if (err >= 0)
+ err = cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY,
+ EINVAL);
+
+ goto failed;
+ }
+
cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len);
if (!cmd) {
err = -ENOMEM;
bacpy(&reply.bdaddr, &cp->bdaddr);
reply.pin_len = cp->pin_len;
- memcpy(reply.pin_code, cp->pin_code, 16);
+ memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code));
err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply);
if (err < 0)
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
{
struct hci_dev *hdev;
struct mgmt_cp_pin_code_neg_reply *cp;
- struct pending_cmd *cmd;
int err;
BT_DBG("");
return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY,
goto failed;
}
- cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, index,
- data, len);
- if (!cmd) {
- err = -ENOMEM;
- goto failed;
- }
-
- err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, sizeof(cp->bdaddr),
- &cp->bdaddr);
- if (err < 0)
- mgmt_pending_remove(cmd);
+ err = send_pin_code_neg_reply(sk, index, hdev, cp);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
hdev->io_capability = cp->io_capability;
BT_DBG("%s IO capability set to 0x%02x", hdev->name,
hdev->io_capability);
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0);
if (!hdev)
return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (cp->io_cap == 0x03) {
sec_level = BT_SECURITY_MEDIUM;
err = 0;
unlock:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, mgmt_op, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, mgmt_op, ENETDOWN);
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
if (!hdev)
return cmd_status(sk, index, MGMT_OP_SET_LOCAL_NAME, ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, index, data, len);
if (!cmd) {
mgmt_pending_remove(cmd);
failed:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
return cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
if (!test_bit(HCI_UP, &hdev->flags)) {
err = cmd_status(sk, index, MGMT_OP_READ_LOCAL_OOB_DATA,
mgmt_pending_remove(cmd);
unlock:
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
return cmd_status(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA,
ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
err = hci_add_remote_oob_data(hdev, &cp->bdaddr, cp->hash,
cp->randomizer);
err = cmd_complete(sk, index, MGMT_OP_ADD_REMOTE_OOB_DATA, NULL,
0);
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
return cmd_status(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
ENODEV);
- hci_dev_lock(hdev);
+ hci_dev_lock_bh(hdev);
err = hci_remove_remote_oob_data(hdev, &cp->bdaddr);
if (err < 0)
err = cmd_complete(sk, index, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
NULL, 0);
- hci_dev_unlock(hdev);
+ hci_dev_unlock_bh(hdev);
hci_dev_put(hdev);
return err;
return err;
}
+static int block_device(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_block_device *cp;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ cp = (void *) data;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE,
+ ENODEV);
+
+ err = hci_blacklist_add(hdev, &cp->bdaddr);
+
+ if (err < 0)
+ err = cmd_status(sk, index, MGMT_OP_BLOCK_DEVICE, -err);
+ else
+ err = cmd_complete(sk, index, MGMT_OP_BLOCK_DEVICE,
+ NULL, 0);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
+static int unblock_device(struct sock *sk, u16 index, unsigned char *data,
+ u16 len)
+{
+ struct hci_dev *hdev;
+ struct mgmt_cp_unblock_device *cp;
+ int err;
+
+ BT_DBG("hci%u", index);
+
+ cp = (void *) data;
+
+ if (len != sizeof(*cp))
+ return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+ EINVAL);
+
+ hdev = hci_dev_get(index);
+ if (!hdev)
+ return cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+ ENODEV);
+
+ err = hci_blacklist_del(hdev, &cp->bdaddr);
+
+ if (err < 0)
+ err = cmd_status(sk, index, MGMT_OP_UNBLOCK_DEVICE, -err);
+ else
+ err = cmd_complete(sk, index, MGMT_OP_UNBLOCK_DEVICE,
+ NULL, 0);
+ hci_dev_put(hdev);
+
+ return err;
+}
+
int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
{
unsigned char *buf;
case MGMT_OP_STOP_DISCOVERY:
err = stop_discovery(sk, index);
break;
+ case MGMT_OP_BLOCK_DEVICE:
+ err = block_device(sk, index, buf + sizeof(*hdr), len);
+ break;
+ case MGMT_OP_UNBLOCK_DEVICE:
+ err = unblock_device(sk, index, buf + sizeof(*hdr), len);
+ break;
default:
BT_DBG("Unknown op %u", opcode);
err = cmd_status(sk, index, opcode, 0x01);
int mgmt_new_key(u16 index, struct link_key *key, u8 persistent)
{
- struct mgmt_ev_new_key ev;
+ struct mgmt_ev_new_key *ev;
+ int err, total;
- memset(&ev, 0, sizeof(ev));
+ total = sizeof(struct mgmt_ev_new_key) + key->dlen;
+ ev = kzalloc(total, GFP_ATOMIC);
+ if (!ev)
+ return -ENOMEM;
- ev.store_hint = persistent;
- bacpy(&ev.key.bdaddr, &key->bdaddr);
- ev.key.type = key->type;
- memcpy(ev.key.val, key->val, 16);
- ev.key.pin_len = key->pin_len;
+ bacpy(&ev->key.bdaddr, &key->bdaddr);
+ ev->key.type = key->type;
+ memcpy(ev->key.val, key->val, 16);
+ ev->key.pin_len = key->pin_len;
+ ev->key.dlen = key->dlen;
+ ev->store_hint = persistent;
- return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL);
+ memcpy(ev->key.data, key->data, key->dlen);
+
+ err = mgmt_event(MGMT_EV_NEW_KEY, index, ev, total, NULL);
+
+ kfree(ev);
+
+ return err;
}
int mgmt_connected(u16 index, bdaddr_t *bdaddr)