X-Git-Url: https://git.karo-electronics.de/?a=blobdiff_plain;f=net%2Fbluetooth%2Fmgmt.c;h=0054c74e27b7fcd4971477408721b003e773f37a;hb=f2e1fbb5f2177227f71c4fc0491e531dd7acd385;hp=48f266a64caf9edc3d1e938380cc7223e9b3b460;hpb=8962ee74be48df16027100f657b2b12e8ef3d34d;p=karo-tx-linux.git diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 48f266a64caf..0054c74e27b7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -22,7 +22,7 @@ /* Bluetooth HCI Management interface */ -#include +#include #include #include @@ -38,17 +38,18 @@ struct pending_cmd { int index; void *cmd; struct sock *sk; + void *user_data; }; LIST_HEAD(cmd_list); -static int cmd_status(struct sock *sk, u16 cmd, u8 status) +static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_status *ev; - BT_DBG("sock %p", sk); + BT_DBG("sock %p, index %u, cmd %u, status %u", sk, index, cmd, status); skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC); if (!skb) @@ -57,6 +58,7 @@ static int cmd_status(struct sock *sk, u16 cmd, u8 status) hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS); + hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(sizeof(*ev)); ev = (void *) skb_put(skb, sizeof(*ev)); @@ -69,29 +71,30 @@ static int cmd_status(struct sock *sk, u16 cmd, u8 status) return 0; } -static int read_version(struct sock *sk) +static int cmd_complete(struct sock *sk, u16 index, u16 cmd, void *rp, + size_t rp_len) { struct sk_buff *skb; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *ev; - struct mgmt_rp_read_version *rp; BT_DBG("sock %p", sk); - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); + skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + rp_len, GFP_ATOMIC); if (!skb) return -ENOMEM; hdr = (void *) skb_put(skb, sizeof(*hdr)); + hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); + hdr->index = cpu_to_le16(index); + hdr->len = cpu_to_le16(sizeof(*ev) + rp_len); - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode); + ev = (void *) skb_put(skb, sizeof(*ev) + rp_len); + put_unaligned_le16(cmd, &ev->opcode); - rp = (void *) skb_put(skb, sizeof(*rp)); - rp->version = MGMT_VERSION; - put_unaligned_le16(MGMT_REVISION, &rp->revision); + if (rp) + memcpy(ev->data, rp, rp_len); if (sock_queue_rcv_skb(sk, skb) < 0) kfree_skb(skb); @@ -99,16 +102,26 @@ static int read_version(struct sock *sk) return 0; } +static int read_version(struct sock *sk) +{ + struct mgmt_rp_read_version rp; + + BT_DBG("sock %p", sk); + + rp.version = MGMT_VERSION; + put_unaligned_le16(MGMT_REVISION, &rp.revision); + + return cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, &rp, + sizeof(rp)); +} + static int read_index_list(struct sock *sk) { - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; struct mgmt_rp_read_index_list *rp; struct list_head *p; - size_t body_len; + size_t rp_len; u16 count; - int i; + int i, err; BT_DBG("sock %p", sk); @@ -119,21 +132,13 @@ static int read_index_list(struct sock *sk) count++; } - body_len = sizeof(*ev) + sizeof(*rp) + (2 * count); - skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC); - if (!skb) { + rp_len = sizeof(*rp) + (2 * count); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { read_unlock(&hci_dev_list_lock); return -ENOMEM; } - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(body_len); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode); - - rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count)); put_unaligned_le16(count, &rp->num_controllers); i = 0; @@ -153,50 +158,24 @@ static int read_index_list(struct sock *sk) read_unlock(&hci_dev_list_lock); - if (sock_queue_rcv_skb(sk, skb) < 0) - kfree_skb(skb); + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, rp, + rp_len); - return 0; + kfree(rp); + + return err; } -static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) +static int read_controller_info(struct sock *sk, u16 index) { - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - struct mgmt_rp_read_info *rp; - struct mgmt_cp_read_info *cp; + struct mgmt_rp_read_info rp; struct hci_dev *hdev; - u16 dev_id; - - BT_DBG("sock %p", sk); - - if (len != 2) - return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL); - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode); - - rp = (void *) skb_put(skb, sizeof(*rp)); - cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - - BT_DBG("request for hci%u", dev_id); + BT_DBG("sock %p hci%u", sk, index); - hdev = hci_dev_get(dev_id); - if (!hdev) { - kfree_skb(skb); - return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV); - } + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_READ_INFO, ENODEV); hci_del_off_timer(hdev); @@ -204,35 +183,31 @@ static int read_controller_info(struct sock *sk, unsigned char *data, u16 len) set_bit(HCI_MGMT, &hdev->flags); - put_unaligned_le16(hdev->id, &rp->index); - rp->type = hdev->dev_type; + rp.type = hdev->dev_type; - rp->powered = test_bit(HCI_UP, &hdev->flags); - rp->connectable = test_bit(HCI_PSCAN, &hdev->flags); - rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags); - rp->pairable = test_bit(HCI_PSCAN, &hdev->flags); + rp.powered = test_bit(HCI_UP, &hdev->flags); + rp.connectable = test_bit(HCI_PSCAN, &hdev->flags); + rp.discoverable = test_bit(HCI_ISCAN, &hdev->flags); + rp.pairable = test_bit(HCI_PSCAN, &hdev->flags); if (test_bit(HCI_AUTH, &hdev->flags)) - rp->sec_mode = 3; + rp.sec_mode = 3; else if (hdev->ssp_mode > 0) - rp->sec_mode = 4; + rp.sec_mode = 4; else - rp->sec_mode = 2; + rp.sec_mode = 2; - bacpy(&rp->bdaddr, &hdev->bdaddr); - memcpy(rp->features, hdev->features, 8); - memcpy(rp->dev_class, hdev->dev_class, 3); - put_unaligned_le16(hdev->manufacturer, &rp->manufacturer); - rp->hci_ver = hdev->hci_ver; - put_unaligned_le16(hdev->hci_rev, &rp->hci_rev); + bacpy(&rp.bdaddr, &hdev->bdaddr); + memcpy(rp.features, hdev->features, 8); + memcpy(rp.dev_class, hdev->dev_class, 3); + put_unaligned_le16(hdev->manufacturer, &rp.manufacturer); + rp.hci_ver = hdev->hci_ver; + put_unaligned_le16(hdev->hci_rev, &rp.hci_rev); hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - if (sock_queue_rcv_skb(sk, skb) < 0) - kfree_skb(skb); - - return 0; + return cmd_complete(sk, index, MGMT_OP_READ_INFO, &rp, sizeof(rp)); } static void mgmt_pending_free(struct pending_cmd *cmd) @@ -242,14 +217,14 @@ static void mgmt_pending_free(struct pending_cmd *cmd) kfree(cmd); } -static int mgmt_pending_add(struct sock *sk, u16 opcode, int index, - void *data, u16 len) +static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, + u16 index, void *data, u16 len) { struct pending_cmd *cmd; cmd = kmalloc(sizeof(*cmd), GFP_ATOMIC); if (!cmd) - return -ENOMEM; + return NULL; cmd->opcode = opcode; cmd->index = index; @@ -257,7 +232,7 @@ static int mgmt_pending_add(struct sock *sk, u16 opcode, int index, cmd->cmd = kmalloc(len, GFP_ATOMIC); if (!cmd->cmd) { kfree(cmd); - return -ENOMEM; + return NULL; } memcpy(cmd->cmd, data, len); @@ -267,7 +242,7 @@ static int mgmt_pending_add(struct sock *sk, u16 opcode, int index, list_add(&cmd->list, &cmd_list); - return 0; + return cmd; } static void mgmt_pending_foreach(u16 opcode, int index, @@ -312,103 +287,106 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, int index) return NULL; } -static void mgmt_pending_remove(u16 opcode, int index) +static void mgmt_pending_remove(struct pending_cmd *cmd) { - struct pending_cmd *cmd; - - cmd = mgmt_pending_find(opcode, index); - if (cmd == NULL) - return; - list_del(&cmd->list); mgmt_pending_free(cmd); } -static int set_powered(struct sock *sk, unsigned char *data, u16 len) +static int set_powered(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct mgmt_mode *cp; struct hci_dev *hdev; - u16 dev_id; - int ret, up; + struct pending_cmd *cmd; + int err, up; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_POWERED, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_POWERED, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_POWERED, ENODEV); hci_dev_lock_bh(hdev); up = test_bit(HCI_UP, &hdev->flags); if ((cp->val && up) || (!cp->val && !up)) { - ret = cmd_status(sk, MGMT_OP_SET_POWERED, EALREADY); + err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EALREADY); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_POWERED, dev_id)) { - ret = cmd_status(sk, MGMT_OP_SET_POWERED, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_POWERED, index)) { + err = cmd_status(sk, index, MGMT_OP_SET_POWERED, EBUSY); goto failed; } - ret = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, dev_id, data, len); - if (ret < 0) + cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, index, data, len); + if (!cmd) { + err = -ENOMEM; goto failed; + } if (cp->val) queue_work(hdev->workqueue, &hdev->power_on); else queue_work(hdev->workqueue, &hdev->power_off); - ret = 0; + err = 0; failed: hci_dev_unlock_bh(hdev); hci_dev_put(hdev); - return ret; + return err; } -static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) +static int set_discoverable(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_mode *cp; struct hci_dev *hdev; - u16 dev_id; + struct pending_cmd *cmd; u8 scan; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, ENETDOWN); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EBUSY); goto failed; } if (cp->val == test_bit(HCI_ISCAN, &hdev->flags) && test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_DISCOVERABLE, EALREADY); + err = cmd_status(sk, index, MGMT_OP_SET_DISCOVERABLE, EALREADY); goto failed; } - err = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, dev_id, data, len); - if (err < 0) + cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, index, data, len); + if (!cmd) { + err = -ENOMEM; goto failed; + } scan = SCAN_PAGE; @@ -417,7 +395,7 @@ static int set_discoverable(struct sock *sk, unsigned char *data, u16 len) err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) - mgmt_pending_remove(MGMT_OP_SET_DISCOVERABLE, dev_id); + mgmt_pending_remove(cmd); failed: hci_dev_unlock_bh(hdev); @@ -426,44 +404,49 @@ failed: return err; } -static int set_connectable(struct sock *sk, unsigned char *data, u16 len) +static int set_connectable(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_mode *cp; struct hci_dev *hdev; - u16 dev_id; + struct pending_cmd *cmd; u8 scan; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, ENETDOWN); goto failed; } - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, dev_id) || - mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, dev_id)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EBUSY); + if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, index) || + mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, index)) { + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EBUSY); goto failed; } if (cp->val == test_bit(HCI_PSCAN, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_SET_CONNECTABLE, EALREADY); + err = cmd_status(sk, index, MGMT_OP_SET_CONNECTABLE, EALREADY); goto failed; } - err = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, dev_id, data, len); - if (err < 0) + cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, index, data, len); + if (!cmd) { + err = -ENOMEM; goto failed; + } if (cp->val) scan = SCAN_PAGE; @@ -472,7 +455,7 @@ static int set_connectable(struct sock *sk, unsigned char *data, u16 len) err = hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); if (err < 0) - mgmt_pending_remove(MGMT_OP_SET_CONNECTABLE, dev_id); + mgmt_pending_remove(cmd); failed: hci_dev_unlock_bh(hdev); @@ -481,7 +464,8 @@ failed: return err; } -static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) +static int mgmt_event(u16 event, u16 index, void *data, u16 data_len, + struct sock *skip_sk) { struct sk_buff *skb; struct mgmt_hdr *hdr; @@ -494,9 +478,11 @@ static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) hdr = (void *) skb_put(skb, sizeof(*hdr)); hdr->opcode = cpu_to_le16(event); + hdr->index = cpu_to_le16(index); hdr->len = cpu_to_le16(data_len); - memcpy(skb_put(skb, data_len), data, data_len); + if (data) + memcpy(skb_put(skb, data_len), data, data_len); hci_send_to_sock(NULL, skb, skip_sk); kfree_skb(skb); @@ -506,47 +492,30 @@ static int mgmt_event(u16 event, void *data, u16 data_len, struct sock *skip_sk) static int send_mode_rsp(struct sock *sk, u16 opcode, u16 index, u8 val) { - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - struct mgmt_mode *rp; - struct sk_buff *skb; - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(opcode, &ev->opcode); + struct mgmt_mode rp; - rp = (void *) skb_put(skb, sizeof(*rp)); - put_unaligned_le16(index, &rp->index); - rp->val = val; + rp.val = val; - if (sock_queue_rcv_skb(sk, skb) < 0) - kfree_skb(skb); - - return 0; + return cmd_complete(sk, index, opcode, &rp, sizeof(rp)); } -static int set_pairable(struct sock *sk, unsigned char *data, u16 len) +static int set_pairable(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct mgmt_mode *cp, ev; struct hci_dev *hdev; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_PAIRABLE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_PAIRABLE, ENODEV); hci_dev_lock_bh(hdev); @@ -555,14 +524,13 @@ static int set_pairable(struct sock *sk, unsigned char *data, u16 len) else clear_bit(HCI_PAIRABLE, &hdev->flags); - err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, dev_id, cp->val); + err = send_mode_rsp(sk, MGMT_OP_SET_PAIRABLE, index, cp->val); if (err < 0) goto failed; - put_unaligned_le16(dev_id, &ev.index); ev.val = cp->val; - err = mgmt_event(MGMT_EV_PAIRABLE, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_PAIRABLE, index, &ev, sizeof(ev), sk); failed: hci_dev_unlock_bh(hdev); @@ -571,31 +539,6 @@ failed: return err; } -static int index_rsp(struct sock *sk, u16 opcode, u16 index) -{ - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - struct sk_buff *skb; - - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(index), GFP_ATOMIC); - if (!skb) - return -ENOMEM; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(index)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(opcode, &ev->opcode); - - put_unaligned_le16(index, skb_put(skb, sizeof(index))); - - if (sock_queue_rcv_skb(sk, skb) < 0) - kfree_skb(skb); - - return 0; -} - static u8 get_service_classes(struct hci_dev *hdev) { struct list_head *p; @@ -629,22 +572,23 @@ static int update_class(struct hci_dev *hdev) return hci_send_cmd(hdev, HCI_OP_WRITE_CLASS_OF_DEV, sizeof(cod), cod); } -static int add_uuid(struct sock *sk, unsigned char *data, u16 len) +static int add_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct mgmt_cp_add_uuid *cp; struct hci_dev *hdev; struct bt_uuid *uuid; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_ADD_UUID, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_ADD_UUID, ENODEV); + return cmd_status(sk, index, MGMT_OP_ADD_UUID, ENODEV); hci_dev_lock_bh(hdev); @@ -663,7 +607,7 @@ static int add_uuid(struct sock *sk, unsigned char *data, u16 len) if (err < 0) goto failed; - err = index_rsp(sk, MGMT_OP_ADD_UUID, dev_id); + err = cmd_complete(sk, index, MGMT_OP_ADD_UUID, NULL, 0); failed: hci_dev_unlock_bh(hdev); @@ -672,23 +616,24 @@ failed: return err; } -static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) +static int remove_uuid(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct list_head *p, *n; - struct mgmt_cp_add_uuid *cp; + struct mgmt_cp_remove_uuid *cp; struct hci_dev *hdev; u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - u16 dev_id; int err, found; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_REMOVE_UUID, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENODEV); hci_dev_lock_bh(hdev); @@ -710,7 +655,7 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) } if (found == 0) { - err = cmd_status(sk, MGMT_OP_REMOVE_UUID, ENOENT); + err = cmd_status(sk, index, MGMT_OP_REMOVE_UUID, ENOENT); goto unlock; } @@ -718,7 +663,7 @@ static int remove_uuid(struct sock *sk, unsigned char *data, u16 len) if (err < 0) goto unlock; - err = index_rsp(sk, MGMT_OP_REMOVE_UUID, dev_id); + err = cmd_complete(sk, index, MGMT_OP_REMOVE_UUID, NULL, 0); unlock: hci_dev_unlock_bh(hdev); @@ -727,21 +672,23 @@ unlock: return err; } -static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) +static int set_dev_class(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_set_dev_class *cp; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - BT_DBG("request for hci%u", dev_id); + BT_DBG("request for hci%u", index); + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, EINVAL); - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_DEV_CLASS, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_DEV_CLASS, ENODEV); hci_dev_lock_bh(hdev); @@ -751,7 +698,7 @@ static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) err = update_class(hdev); if (err == 0) - err = index_rsp(sk, MGMT_OP_SET_DEV_CLASS, dev_id); + err = cmd_complete(sk, index, MGMT_OP_SET_DEV_CLASS, NULL, 0); hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -759,23 +706,25 @@ static int set_dev_class(struct sock *sk, unsigned char *data, u16 len) return err; } -static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) +static int set_service_cache(struct sock *sk, u16 index, unsigned char *data, + u16 len) { struct hci_dev *hdev; struct mgmt_cp_set_service_cache *cp; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_SET_SERVICE_CACHE, ENODEV); + return cmd_status(sk, index, MGMT_OP_SET_SERVICE_CACHE, ENODEV); hci_dev_lock_bh(hdev); - BT_DBG("hci%u enable %d", dev_id, cp->enable); + BT_DBG("hci%u enable %d", index, cp->enable); if (cp->enable) { set_bit(HCI_SERVICE_CACHE, &hdev->flags); @@ -786,7 +735,8 @@ static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) } if (err == 0) - err = index_rsp(sk, MGMT_OP_SET_SERVICE_CACHE, dev_id); + err = cmd_complete(sk, index, MGMT_OP_SET_SERVICE_CACHE, NULL, + 0); hci_dev_unlock_bh(hdev); hci_dev_put(hdev); @@ -794,15 +744,18 @@ static int set_service_cache(struct sock *sk, unsigned char *data, u16 len) return err; } -static int load_keys(struct sock *sk, unsigned char *data, u16 len) +static int load_keys(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_load_keys *cp; - u16 dev_id, key_count, expected_len; + u16 key_count, expected_len; int i; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); + + if (len < sizeof(*cp)) + return -EINVAL; + key_count = get_unaligned_le16(&cp->key_count); expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_key_info); @@ -812,11 +765,11 @@ static int load_keys(struct sock *sk, unsigned char *data, u16 len) return -EINVAL; } - hdev = hci_dev_get(dev_id); + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_LOAD_KEYS, ENODEV); + return cmd_status(sk, index, MGMT_OP_LOAD_KEYS, ENODEV); - BT_DBG("hci%u debug_keys %u key_count %u", dev_id, cp->debug_keys, + BT_DBG("hci%u debug_keys %u key_count %u", index, cp->debug_keys, key_count); hci_dev_lock_bh(hdev); @@ -843,26 +796,27 @@ static int load_keys(struct sock *sk, unsigned char *data, u16 len) return 0; } -static int remove_key(struct sock *sk, unsigned char *data, u16 len) +static int remove_key(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_remove_key *cp; struct hci_conn *conn; - u16 dev_id; int err; cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_REMOVE_KEY, ENODEV); + return cmd_status(sk, index, MGMT_OP_REMOVE_KEY, ENODEV); hci_dev_lock_bh(hdev); err = hci_remove_link_key(hdev, &cp->bdaddr); if (err < 0) { - err = cmd_status(sk, MGMT_OP_REMOVE_KEY, -err); + err = cmd_status(sk, index, MGMT_OP_REMOVE_KEY, -err); goto unlock; } @@ -887,52 +841,409 @@ unlock: return err; } -static int disconnect(struct sock *sk, unsigned char *data, u16 len) +static int disconnect(struct sock *sk, u16 index, unsigned char *data, u16 len) { struct hci_dev *hdev; struct mgmt_cp_disconnect *cp; struct hci_cp_disconnect dc; + struct pending_cmd *cmd; struct hci_conn *conn; - u16 dev_id; int err; BT_DBG(""); cp = (void *) data; - dev_id = get_unaligned_le16(&cp->index); - hdev = hci_dev_get(dev_id); + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_DISCONNECT, EINVAL); + + hdev = hci_dev_get(index); if (!hdev) - return cmd_status(sk, MGMT_OP_DISCONNECT, ENODEV); + return cmd_status(sk, index, MGMT_OP_DISCONNECT, ENODEV); hci_dev_lock_bh(hdev); if (!test_bit(HCI_UP, &hdev->flags)) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, ENETDOWN); + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENETDOWN); goto failed; } - if (mgmt_pending_find(MGMT_OP_DISCONNECT, dev_id)) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, EBUSY); + if (mgmt_pending_find(MGMT_OP_DISCONNECT, index)) { + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, EBUSY); goto failed; } conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->bdaddr); if (!conn) { - err = cmd_status(sk, MGMT_OP_DISCONNECT, ENOTCONN); + err = cmd_status(sk, index, MGMT_OP_DISCONNECT, ENOTCONN); goto failed; } - err = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, dev_id, data, len); - if (err < 0) + cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, index, data, len); + if (!cmd) { + err = -ENOMEM; goto failed; + } put_unaligned_le16(conn->handle, &dc.handle); dc.reason = 0x13; /* Remote User Terminated Connection */ err = hci_send_cmd(hdev, HCI_OP_DISCONNECT, sizeof(dc), &dc); if (err < 0) - mgmt_pending_remove(MGMT_OP_DISCONNECT, dev_id); + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int get_connections(struct sock *sk, u16 index) +{ + struct mgmt_rp_get_connections *rp; + struct hci_dev *hdev; + struct list_head *p; + size_t rp_len; + u16 count; + int i, err; + + BT_DBG(""); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_GET_CONNECTIONS, ENODEV); + + hci_dev_lock_bh(hdev); + + count = 0; + list_for_each(p, &hdev->conn_hash.list) { + count++; + } + + rp_len = sizeof(*rp) + (count * sizeof(bdaddr_t)); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + err = -ENOMEM; + goto unlock; + } + + 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_bh(hdev); + hci_dev_put(hdev); + return err; +} + +static int pin_code_reply(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_pin_code_reply *cp; + struct hci_cp_pin_code_reply reply; + struct pending_cmd *cmd; + int err; + + BT_DBG(""); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_PIN_CODE_REPLY, ENODEV); + + 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; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, index, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + bacpy(&reply.bdaddr, &cp->bdaddr); + reply.pin_len = cp->pin_len; + memcpy(reply.pin_code, cp->pin_code, 16); + + err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); + if (err < 0) + mgmt_pending_remove(cmd); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int pin_code_neg_reply(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_pin_code_neg_reply *cp; + struct pending_cmd *cmd; + int err; + + BT_DBG(""); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, + EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, + ENODEV); + + hci_dev_lock_bh(hdev); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, + ENETDOWN); + 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); + +failed: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int set_io_capability(struct sock *sk, u16 index, unsigned char *data, + u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_set_io_capability *cp; + + BT_DBG(""); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_SET_IO_CAPABILITY, ENODEV); + + 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_bh(hdev); + hci_dev_put(hdev); + + return cmd_complete(sk, index, MGMT_OP_SET_IO_CAPABILITY, NULL, 0); +} + +static inline struct pending_cmd *find_pairing(struct hci_conn *conn) +{ + struct hci_dev *hdev = conn->hdev; + struct list_head *p; + + list_for_each(p, &cmd_list) { + struct pending_cmd *cmd; + + cmd = list_entry(p, struct pending_cmd, list); + + if (cmd->opcode != MGMT_OP_PAIR_DEVICE) + continue; + + if (cmd->index != hdev->id) + continue; + + if (cmd->user_data != conn) + continue; + + return cmd; + } + + return NULL; +} + +static void pairing_complete(struct pending_cmd *cmd, u8 status) +{ + struct mgmt_rp_pair_device rp; + struct hci_conn *conn = cmd->user_data; + + bacpy(&rp.bdaddr, &conn->dst); + rp.status = status; + + cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, &rp, sizeof(rp)); + + /* So we don't get further callbacks for this connection */ + conn->connect_cfm_cb = NULL; + conn->security_cfm_cb = NULL; + conn->disconn_cfm_cb = NULL; + + hci_conn_put(conn); + + mgmt_pending_remove(cmd); +} + +static void pairing_complete_cb(struct hci_conn *conn, u8 status) +{ + struct pending_cmd *cmd; + + BT_DBG("status %u", status); + + cmd = find_pairing(conn); + if (!cmd) { + BT_DBG("Unable to find a pending command"); + return; + } + + pairing_complete(cmd, status); +} + +static int pair_device(struct sock *sk, u16 index, unsigned char *data, u16 len) +{ + struct hci_dev *hdev; + struct mgmt_cp_pair_device *cp; + struct pending_cmd *cmd; + u8 sec_level, auth_type; + struct hci_conn *conn; + int err; + + BT_DBG(""); + + cp = (void *) data; + + if (len != sizeof(*cp)) + return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, ENODEV); + + hci_dev_lock_bh(hdev); + + if (cp->io_cap == 0x03) { + sec_level = BT_SECURITY_MEDIUM; + auth_type = HCI_AT_DEDICATED_BONDING; + } else { + sec_level = BT_SECURITY_HIGH; + auth_type = HCI_AT_DEDICATED_BONDING_MITM; + } + + conn = hci_connect(hdev, ACL_LINK, &cp->bdaddr, sec_level, auth_type); + if (IS_ERR(conn)) { + err = PTR_ERR(conn); + goto unlock; + } + + if (conn->connect_cfm_cb) { + hci_conn_put(conn); + err = cmd_status(sk, index, MGMT_OP_PAIR_DEVICE, EBUSY); + goto unlock; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, index, data, len); + if (!cmd) { + err = -ENOMEM; + hci_conn_put(conn); + goto unlock; + } + + conn->connect_cfm_cb = pairing_complete_cb; + conn->security_cfm_cb = pairing_complete_cb; + conn->disconn_cfm_cb = pairing_complete_cb; + conn->io_capability = cp->io_cap; + cmd->user_data = conn; + + if (conn->state == BT_CONNECTED && + hci_conn_security(conn, sec_level, auth_type)) + pairing_complete(cmd, 0); + + err = 0; + +unlock: + hci_dev_unlock_bh(hdev); + hci_dev_put(hdev); + + return err; +} + +static int user_confirm_reply(struct sock *sk, u16 index, unsigned char *data, + u16 len, int success) +{ + struct mgmt_cp_user_confirm_reply *cp = (void *) data; + u16 mgmt_op, hci_op; + struct pending_cmd *cmd; + struct hci_dev *hdev; + int err; + + BT_DBG(""); + + if (success) { + mgmt_op = MGMT_OP_USER_CONFIRM_REPLY; + hci_op = HCI_OP_USER_CONFIRM_REPLY; + } else { + mgmt_op = MGMT_OP_USER_CONFIRM_NEG_REPLY; + hci_op = HCI_OP_USER_CONFIRM_NEG_REPLY; + } + + if (len != sizeof(*cp)) + return cmd_status(sk, index, mgmt_op, EINVAL); + + hdev = hci_dev_get(index); + if (!hdev) + return cmd_status(sk, index, mgmt_op, ENODEV); + + if (!test_bit(HCI_UP, &hdev->flags)) { + err = cmd_status(sk, index, mgmt_op, ENETDOWN); + goto failed; + } + + cmd = mgmt_pending_add(sk, mgmt_op, index, data, len); + if (!cmd) { + err = -ENOMEM; + goto failed; + } + + err = hci_send_cmd(hdev, hci_op, sizeof(cp->bdaddr), &cp->bdaddr); + if (err < 0) + mgmt_pending_remove(cmd); failed: hci_dev_unlock_bh(hdev); @@ -945,7 +1256,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { unsigned char *buf; struct mgmt_hdr *hdr; - u16 opcode, len; + u16 opcode, index, len; int err; BT_DBG("got %zu bytes", msglen); @@ -964,6 +1275,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) hdr = (struct mgmt_hdr *) buf; opcode = get_unaligned_le16(&hdr->opcode); + index = get_unaligned_le16(&hdr->index); len = get_unaligned_le16(&hdr->len); if (len != msglen - sizeof(*hdr)) { @@ -979,44 +1291,65 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) err = read_index_list(sk); break; case MGMT_OP_READ_INFO: - err = read_controller_info(sk, buf + sizeof(*hdr), len); + err = read_controller_info(sk, index); break; case MGMT_OP_SET_POWERED: - err = set_powered(sk, buf + sizeof(*hdr), len); + err = set_powered(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_DISCOVERABLE: - err = set_discoverable(sk, buf + sizeof(*hdr), len); + err = set_discoverable(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_CONNECTABLE: - err = set_connectable(sk, buf + sizeof(*hdr), len); + err = set_connectable(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_PAIRABLE: - err = set_pairable(sk, buf + sizeof(*hdr), len); + err = set_pairable(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_ADD_UUID: - err = add_uuid(sk, buf + sizeof(*hdr), len); + err = add_uuid(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_REMOVE_UUID: - err = remove_uuid(sk, buf + sizeof(*hdr), len); + err = remove_uuid(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_DEV_CLASS: - err = set_dev_class(sk, buf + sizeof(*hdr), len); + err = set_dev_class(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_SET_SERVICE_CACHE: - err = set_service_cache(sk, buf + sizeof(*hdr), len); + err = set_service_cache(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_LOAD_KEYS: - err = load_keys(sk, buf + sizeof(*hdr), len); + err = load_keys(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_REMOVE_KEY: - err = remove_key(sk, buf + sizeof(*hdr), len); + err = remove_key(sk, index, buf + sizeof(*hdr), len); break; case MGMT_OP_DISCONNECT: - err = disconnect(sk, buf + sizeof(*hdr), len); + err = disconnect(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_GET_CONNECTIONS: + err = get_connections(sk, index); + break; + case MGMT_OP_PIN_CODE_REPLY: + err = pin_code_reply(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_PIN_CODE_NEG_REPLY: + err = pin_code_neg_reply(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_SET_IO_CAPABILITY: + err = set_io_capability(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_PAIR_DEVICE: + err = pair_device(sk, index, buf + sizeof(*hdr), len); + break; + case MGMT_OP_USER_CONFIRM_REPLY: + err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 1); + break; + case MGMT_OP_USER_CONFIRM_NEG_REPLY: + err = user_confirm_reply(sk, index, buf + sizeof(*hdr), len, 0); break; default: BT_DBG("Unknown op %u", opcode); - err = cmd_status(sk, opcode, 0x01); + err = cmd_status(sk, index, opcode, 0x01); break; } @@ -1032,20 +1365,12 @@ done: int mgmt_index_added(u16 index) { - struct mgmt_ev_index_added ev; - - put_unaligned_le16(index, &ev.index); - - return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_INDEX_ADDED, index, NULL, 0, NULL); } int mgmt_index_removed(u16 index) { - struct mgmt_ev_index_added ev; - - put_unaligned_le16(index, &ev.index); - - return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_INDEX_REMOVED, index, NULL, 0, NULL); } struct cmd_lookup { @@ -1081,10 +1406,9 @@ int mgmt_powered(u16 index, u8 powered) mgmt_pending_foreach(MGMT_OP_SET_POWERED, index, mode_rsp, &match); - put_unaligned_le16(index, &ev.index); ev.val = powered; - ret = mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_POWERED, index, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -1098,13 +1422,12 @@ int mgmt_discoverable(u16 index, u8 discoverable) struct cmd_lookup match = { discoverable, NULL }; int ret; - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, - mode_rsp, &match); + mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, index, mode_rsp, &match); - put_unaligned_le16(index, &ev.index); ev.val = discoverable; - ret = mgmt_event(MGMT_EV_DISCOVERABLE, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_DISCOVERABLE, index, &ev, sizeof(ev), + match.sk); if (match.sk) sock_put(match.sk); @@ -1120,10 +1443,9 @@ int mgmt_connectable(u16 index, u8 connectable) mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, index, mode_rsp, &match); - put_unaligned_le16(index, &ev.index); ev.val = connectable; - ret = mgmt_event(MGMT_EV_CONNECTABLE, &ev, sizeof(ev), match.sk); + ret = mgmt_event(MGMT_EV_CONNECTABLE, index, &ev, sizeof(ev), match.sk); if (match.sk) sock_put(match.sk); @@ -1137,59 +1459,38 @@ int mgmt_new_key(u16 index, struct link_key *key, u8 old_key_type) memset(&ev, 0, sizeof(ev)); - put_unaligned_le16(index, &ev.index); - 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.old_key_type = old_key_type; - return mgmt_event(MGMT_EV_NEW_KEY, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_NEW_KEY, index, &ev, sizeof(ev), NULL); } int mgmt_connected(u16 index, bdaddr_t *bdaddr) { struct mgmt_ev_connected ev; - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); - return mgmt_event(MGMT_EV_CONNECTED, &ev, sizeof(ev), NULL); + return mgmt_event(MGMT_EV_CONNECTED, index, &ev, sizeof(ev), NULL); } static void disconnect_rsp(struct pending_cmd *cmd, void *data) { struct mgmt_cp_disconnect *cp = cmd->cmd; struct sock **sk = data; - struct sk_buff *skb; - struct mgmt_hdr *hdr; - struct mgmt_ev_cmd_complete *ev; - struct mgmt_rp_disconnect *rp; + struct mgmt_rp_disconnect rp; - skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC); - if (!skb) - return; - - hdr = (void *) skb_put(skb, sizeof(*hdr)); - hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE); - hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp)); - - ev = (void *) skb_put(skb, sizeof(*ev)); - put_unaligned_le16(MGMT_OP_DISCONNECT, &ev->opcode); + bacpy(&rp.bdaddr, &cp->bdaddr); - rp = (void *) skb_put(skb, sizeof(*rp)); - put_unaligned_le16(cmd->index, &rp->index); - bacpy(&rp->bdaddr, &cp->bdaddr); - - if (sock_queue_rcv_skb(cmd->sk, skb) < 0) - kfree_skb(skb); + cmd_complete(cmd->sk, cmd->index, MGMT_OP_DISCONNECT, &rp, sizeof(rp)); *sk = cmd->sk; sock_hold(*sk); - list_del(&cmd->list); - mgmt_pending_free(cmd); + mgmt_pending_remove(cmd); } int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) @@ -1200,10 +1501,9 @@ int mgmt_disconnected(u16 index, bdaddr_t *bdaddr) mgmt_pending_foreach(MGMT_OP_DISCONNECT, index, disconnect_rsp, &sk); - put_unaligned_le16(index, &ev.index); bacpy(&ev.bdaddr, bdaddr); - err = mgmt_event(MGMT_EV_DISCONNECTED, &ev, sizeof(ev), sk); + err = mgmt_event(MGMT_EV_DISCONNECTED, index, &ev, sizeof(ev), sk); if (sk) sock_put(sk); @@ -1220,10 +1520,126 @@ int mgmt_disconnect_failed(u16 index) if (!cmd) return -ENOENT; - err = cmd_status(cmd->sk, MGMT_OP_DISCONNECT, EIO); + err = cmd_status(cmd->sk, index, MGMT_OP_DISCONNECT, EIO); - list_del(&cmd->list); - mgmt_pending_free(cmd); + mgmt_pending_remove(cmd); return err; } + +int mgmt_connect_failed(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct mgmt_ev_connect_failed ev; + + bacpy(&ev.bdaddr, bdaddr); + ev.status = status; + + return mgmt_event(MGMT_EV_CONNECT_FAILED, index, &ev, sizeof(ev), NULL); +} + +int mgmt_pin_code_request(u16 index, bdaddr_t *bdaddr) +{ + struct mgmt_ev_pin_code_request ev; + + bacpy(&ev.bdaddr, bdaddr); + + return mgmt_event(MGMT_EV_PIN_CODE_REQUEST, index, &ev, sizeof(ev), + NULL); +} + +int mgmt_pin_code_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct pending_cmd *cmd; + struct mgmt_rp_pin_code_reply rp; + int err; + + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_REPLY, index); + if (!cmd) + return -ENOENT; + + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + + err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_REPLY, &rp, + sizeof(rp)); + + mgmt_pending_remove(cmd); + + return err; +} + +int mgmt_pin_code_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct pending_cmd *cmd; + struct mgmt_rp_pin_code_reply rp; + int err; + + cmd = mgmt_pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, index); + if (!cmd) + return -ENOENT; + + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + + err = cmd_complete(cmd->sk, index, MGMT_OP_PIN_CODE_NEG_REPLY, &rp, + sizeof(rp)); + + mgmt_pending_remove(cmd); + + return err; +} + +int mgmt_user_confirm_request(u16 index, bdaddr_t *bdaddr, __le32 value) +{ + struct mgmt_ev_user_confirm_request ev; + + BT_DBG("hci%u", index); + + bacpy(&ev.bdaddr, bdaddr); + put_unaligned_le32(value, &ev.value); + + return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, index, &ev, sizeof(ev), + NULL); +} + +static int confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status, + u8 opcode) +{ + struct pending_cmd *cmd; + struct mgmt_rp_user_confirm_reply rp; + int err; + + cmd = mgmt_pending_find(opcode, index); + if (!cmd) + return -ENOENT; + + bacpy(&rp.bdaddr, bdaddr); + rp.status = status; + err = cmd_complete(cmd->sk, index, opcode, &rp, sizeof(rp)); + + mgmt_pending_remove(cmd); + + return err; +} + +int mgmt_user_confirm_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + return confirm_reply_complete(index, bdaddr, status, + MGMT_OP_USER_CONFIRM_REPLY); +} + +int mgmt_user_confirm_neg_reply_complete(u16 index, bdaddr_t *bdaddr, u8 status) +{ + return confirm_reply_complete(index, bdaddr, status, + MGMT_OP_USER_CONFIRM_NEG_REPLY); +} + +int mgmt_auth_failed(u16 index, bdaddr_t *bdaddr, u8 status) +{ + struct mgmt_ev_auth_failed ev; + + bacpy(&ev.bdaddr, bdaddr); + ev.status = status; + + return mgmt_event(MGMT_EV_AUTH_FAILED, index, &ev, sizeof(ev), NULL); +}