linux/net/bluetooth/mgmt.c
<<
>>
Prefs
   1/*
   2   BlueZ - Bluetooth protocol stack for Linux
   3   Copyright (C) 2010  Nokia Corporation
   4
   5   This program is free software; you can redistribute it and/or modify
   6   it under the terms of the GNU General Public License version 2 as
   7   published by the Free Software Foundation;
   8
   9   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  10   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  11   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
  12   IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY
  13   CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES
  14   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  15   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  16   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  17
  18   ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS,
  19   COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS
  20   SOFTWARE IS DISCLAIMED.
  21*/
  22
  23/* Bluetooth HCI Management interface */
  24
  25#include <asm/uaccess.h>
  26#include <asm/unaligned.h>
  27
  28#include <net/bluetooth/bluetooth.h>
  29#include <net/bluetooth/hci_core.h>
  30#include <net/bluetooth/mgmt.h>
  31
  32#define MGMT_VERSION    0
  33#define MGMT_REVISION   1
  34
  35static int cmd_status(struct sock *sk, u16 cmd, u8 status)
  36{
  37        struct sk_buff *skb;
  38        struct mgmt_hdr *hdr;
  39        struct mgmt_ev_cmd_status *ev;
  40
  41        BT_DBG("sock %p", sk);
  42
  43        skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
  44        if (!skb)
  45                return -ENOMEM;
  46
  47        hdr = (void *) skb_put(skb, sizeof(*hdr));
  48
  49        hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
  50        hdr->len = cpu_to_le16(sizeof(*ev));
  51
  52        ev = (void *) skb_put(skb, sizeof(*ev));
  53        ev->status = status;
  54        put_unaligned_le16(cmd, &ev->opcode);
  55
  56        if (sock_queue_rcv_skb(sk, skb) < 0)
  57                kfree_skb(skb);
  58
  59        return 0;
  60}
  61
  62static int read_version(struct sock *sk)
  63{
  64        struct sk_buff *skb;
  65        struct mgmt_hdr *hdr;
  66        struct mgmt_ev_cmd_complete *ev;
  67        struct mgmt_rp_read_version *rp;
  68
  69        BT_DBG("sock %p", sk);
  70
  71        skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
  72        if (!skb)
  73                return -ENOMEM;
  74
  75        hdr = (void *) skb_put(skb, sizeof(*hdr));
  76        hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
  77        hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
  78
  79        ev = (void *) skb_put(skb, sizeof(*ev));
  80        put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode);
  81
  82        rp = (void *) skb_put(skb, sizeof(*rp));
  83        rp->version = MGMT_VERSION;
  84        put_unaligned_le16(MGMT_REVISION, &rp->revision);
  85
  86        if (sock_queue_rcv_skb(sk, skb) < 0)
  87                kfree_skb(skb);
  88
  89        return 0;
  90}
  91
  92static int read_index_list(struct sock *sk)
  93{
  94        struct sk_buff *skb;
  95        struct mgmt_hdr *hdr;
  96        struct mgmt_ev_cmd_complete *ev;
  97        struct mgmt_rp_read_index_list *rp;
  98        struct list_head *p;
  99        size_t body_len;
 100        u16 count;
 101        int i;
 102
 103        BT_DBG("sock %p", sk);
 104
 105        read_lock(&hci_dev_list_lock);
 106
 107        count = 0;
 108        list_for_each(p, &hci_dev_list) {
 109                count++;
 110        }
 111
 112        body_len = sizeof(*ev) + sizeof(*rp) + (2 * count);
 113        skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC);
 114        if (!skb)
 115                return -ENOMEM;
 116
 117        hdr = (void *) skb_put(skb, sizeof(*hdr));
 118        hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
 119        hdr->len = cpu_to_le16(body_len);
 120
 121        ev = (void *) skb_put(skb, sizeof(*ev));
 122        put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode);
 123
 124        rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count));
 125        put_unaligned_le16(count, &rp->num_controllers);
 126
 127        i = 0;
 128        list_for_each(p, &hci_dev_list) {
 129                struct hci_dev *d = list_entry(p, struct hci_dev, list);
 130                put_unaligned_le16(d->id, &rp->index[i++]);
 131                BT_DBG("Added hci%u", d->id);
 132        }
 133
 134        read_unlock(&hci_dev_list_lock);
 135
 136        if (sock_queue_rcv_skb(sk, skb) < 0)
 137                kfree_skb(skb);
 138
 139        return 0;
 140}
 141
 142static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
 143{
 144        struct sk_buff *skb;
 145        struct mgmt_hdr *hdr;
 146        struct mgmt_ev_cmd_complete *ev;
 147        struct mgmt_rp_read_info *rp;
 148        struct mgmt_cp_read_info *cp;
 149        struct hci_dev *hdev;
 150        u16 dev_id;
 151
 152        BT_DBG("sock %p", sk);
 153
 154        if (len != 2)
 155                return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
 156
 157        skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
 158        if (!skb)
 159                return -ENOMEM;
 160
 161        hdr = (void *) skb_put(skb, sizeof(*hdr));
 162        hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
 163        hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
 164
 165        ev = (void *) skb_put(skb, sizeof(*ev));
 166        put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
 167
 168        rp = (void *) skb_put(skb, sizeof(*rp));
 169
 170        cp = (void *) data;
 171        dev_id = get_unaligned_le16(&cp->index);
 172
 173        BT_DBG("request for hci%u", dev_id);
 174
 175        hdev = hci_dev_get(dev_id);
 176        if (!hdev) {
 177                kfree_skb(skb);
 178                return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
 179        }
 180
 181        hci_dev_lock_bh(hdev);
 182
 183        put_unaligned_le16(hdev->id, &rp->index);
 184        rp->type = hdev->dev_type;
 185
 186        rp->powered = test_bit(HCI_UP, &hdev->flags);
 187        rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags);
 188        rp->pairable = test_bit(HCI_PSCAN, &hdev->flags);
 189
 190        if (test_bit(HCI_AUTH, &hdev->flags))
 191                rp->sec_mode = 3;
 192        else if (hdev->ssp_mode > 0)
 193                rp->sec_mode = 4;
 194        else
 195                rp->sec_mode = 2;
 196
 197        bacpy(&rp->bdaddr, &hdev->bdaddr);
 198        memcpy(rp->features, hdev->features, 8);
 199        memcpy(rp->dev_class, hdev->dev_class, 3);
 200        put_unaligned_le16(hdev->manufacturer, &rp->manufacturer);
 201        rp->hci_ver = hdev->hci_ver;
 202        put_unaligned_le16(hdev->hci_rev, &rp->hci_rev);
 203
 204        hci_dev_unlock_bh(hdev);
 205        hci_dev_put(hdev);
 206
 207        if (sock_queue_rcv_skb(sk, skb) < 0)
 208                kfree_skb(skb);
 209
 210        return 0;
 211}
 212
 213int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
 214{
 215        unsigned char *buf;
 216        struct mgmt_hdr *hdr;
 217        u16 opcode, len;
 218        int err;
 219
 220        BT_DBG("got %zu bytes", msglen);
 221
 222        if (msglen < sizeof(*hdr))
 223                return -EINVAL;
 224
 225        buf = kmalloc(msglen, GFP_ATOMIC);
 226        if (!buf)
 227                return -ENOMEM;
 228
 229        if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
 230                err = -EFAULT;
 231                goto done;
 232        }
 233
 234        hdr = (struct mgmt_hdr *) buf;
 235        opcode = get_unaligned_le16(&hdr->opcode);
 236        len = get_unaligned_le16(&hdr->len);
 237
 238        if (len != msglen - sizeof(*hdr)) {
 239                err = -EINVAL;
 240                goto done;
 241        }
 242
 243        switch (opcode) {
 244        case MGMT_OP_READ_VERSION:
 245                err = read_version(sk);
 246                break;
 247        case MGMT_OP_READ_INDEX_LIST:
 248                err = read_index_list(sk);
 249                break;
 250        case MGMT_OP_READ_INFO:
 251                err = read_controller_info(sk, buf + sizeof(*hdr), len);
 252                break;
 253        default:
 254                BT_DBG("Unknown op %u", opcode);
 255                err = cmd_status(sk, opcode, 0x01);
 256                break;
 257        }
 258
 259        if (err < 0)
 260                goto done;
 261
 262        err = msglen;
 263
 264done:
 265        kfree(buf);
 266        return err;
 267}
 268
 269static int mgmt_event(u16 event, void *data, u16 data_len)
 270{
 271        struct sk_buff *skb;
 272        struct mgmt_hdr *hdr;
 273
 274        skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
 275        if (!skb)
 276                return -ENOMEM;
 277
 278        bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
 279
 280        hdr = (void *) skb_put(skb, sizeof(*hdr));
 281        hdr->opcode = cpu_to_le16(event);
 282        hdr->len = cpu_to_le16(data_len);
 283
 284        memcpy(skb_put(skb, data_len), data, data_len);
 285
 286        hci_send_to_sock(NULL, skb);
 287        kfree_skb(skb);
 288
 289        return 0;
 290}
 291
 292int mgmt_index_added(u16 index)
 293{
 294        struct mgmt_ev_index_added ev;
 295
 296        put_unaligned_le16(index, &ev.index);
 297
 298        return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev));
 299}
 300
 301int mgmt_index_removed(u16 index)
 302{
 303        struct mgmt_ev_index_added ev;
 304
 305        put_unaligned_le16(index, &ev.index);
 306
 307        return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev));
 308}
 309