2 BlueZ - Bluetooth protocol stack for Linux
3 Copyright (C) 2010 Nokia Corporation
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;
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.
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.
23 /* Bluetooth HCI Management interface */
25 #include <asm/uaccess.h>
26 #include <asm/unaligned.h>
28 #include <net/bluetooth/bluetooth.h>
29 #include <net/bluetooth/hci_core.h>
30 #include <net/bluetooth/mgmt.h>
32 #define MGMT_VERSION 0
33 #define MGMT_REVISION 1
35 static int cmd_status(struct sock *sk, u16 cmd, u8 status)
39 struct mgmt_ev_cmd_status *ev;
41 BT_DBG("sock %p", sk);
43 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev), GFP_ATOMIC);
47 hdr = (void *) skb_put(skb, sizeof(*hdr));
49 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_STATUS);
50 hdr->len = cpu_to_le16(sizeof(*ev));
52 ev = (void *) skb_put(skb, sizeof(*ev));
54 put_unaligned_le16(cmd, &ev->opcode);
56 if (sock_queue_rcv_skb(sk, skb) < 0)
62 static int read_version(struct sock *sk)
66 struct mgmt_ev_cmd_complete *ev;
67 struct mgmt_rp_read_version *rp;
69 BT_DBG("sock %p", sk);
71 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
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));
79 ev = (void *) skb_put(skb, sizeof(*ev));
80 put_unaligned_le16(MGMT_OP_READ_VERSION, &ev->opcode);
82 rp = (void *) skb_put(skb, sizeof(*rp));
83 rp->version = MGMT_VERSION;
84 put_unaligned_le16(MGMT_REVISION, &rp->revision);
86 if (sock_queue_rcv_skb(sk, skb) < 0)
92 static int read_index_list(struct sock *sk)
96 struct mgmt_ev_cmd_complete *ev;
97 struct mgmt_rp_read_index_list *rp;
103 BT_DBG("sock %p", sk);
105 read_lock(&hci_dev_list_lock);
108 list_for_each(p, &hci_dev_list) {
112 body_len = sizeof(*ev) + sizeof(*rp) + (2 * count);
113 skb = alloc_skb(sizeof(*hdr) + body_len, GFP_ATOMIC);
115 read_unlock(&hci_dev_list_lock);
119 hdr = (void *) skb_put(skb, sizeof(*hdr));
120 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
121 hdr->len = cpu_to_le16(body_len);
123 ev = (void *) skb_put(skb, sizeof(*ev));
124 put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode);
126 rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count));
127 put_unaligned_le16(count, &rp->num_controllers);
130 list_for_each(p, &hci_dev_list) {
131 struct hci_dev *d = list_entry(p, struct hci_dev, list);
132 put_unaligned_le16(d->id, &rp->index[i++]);
133 BT_DBG("Added hci%u", d->id);
136 read_unlock(&hci_dev_list_lock);
138 if (sock_queue_rcv_skb(sk, skb) < 0)
144 static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
147 struct mgmt_hdr *hdr;
148 struct mgmt_ev_cmd_complete *ev;
149 struct mgmt_rp_read_info *rp;
150 struct mgmt_cp_read_info *cp;
151 struct hci_dev *hdev;
154 BT_DBG("sock %p", sk);
157 return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
159 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
163 hdr = (void *) skb_put(skb, sizeof(*hdr));
164 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
165 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
167 ev = (void *) skb_put(skb, sizeof(*ev));
168 put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
170 rp = (void *) skb_put(skb, sizeof(*rp));
173 dev_id = get_unaligned_le16(&cp->index);
175 BT_DBG("request for hci%u", dev_id);
177 hdev = hci_dev_get(dev_id);
180 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
183 hci_dev_lock_bh(hdev);
185 put_unaligned_le16(hdev->id, &rp->index);
186 rp->type = hdev->dev_type;
188 rp->powered = test_bit(HCI_UP, &hdev->flags);
189 rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags);
190 rp->pairable = test_bit(HCI_PSCAN, &hdev->flags);
192 if (test_bit(HCI_AUTH, &hdev->flags))
194 else if (hdev->ssp_mode > 0)
199 bacpy(&rp->bdaddr, &hdev->bdaddr);
200 memcpy(rp->features, hdev->features, 8);
201 memcpy(rp->dev_class, hdev->dev_class, 3);
202 put_unaligned_le16(hdev->manufacturer, &rp->manufacturer);
203 rp->hci_ver = hdev->hci_ver;
204 put_unaligned_le16(hdev->hci_rev, &rp->hci_rev);
206 hci_dev_unlock_bh(hdev);
209 if (sock_queue_rcv_skb(sk, skb) < 0)
215 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
218 struct mgmt_hdr *hdr;
222 BT_DBG("got %zu bytes", msglen);
224 if (msglen < sizeof(*hdr))
227 buf = kmalloc(msglen, GFP_ATOMIC);
231 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
236 hdr = (struct mgmt_hdr *) buf;
237 opcode = get_unaligned_le16(&hdr->opcode);
238 len = get_unaligned_le16(&hdr->len);
240 if (len != msglen - sizeof(*hdr)) {
246 case MGMT_OP_READ_VERSION:
247 err = read_version(sk);
249 case MGMT_OP_READ_INDEX_LIST:
250 err = read_index_list(sk);
252 case MGMT_OP_READ_INFO:
253 err = read_controller_info(sk, buf + sizeof(*hdr), len);
256 BT_DBG("Unknown op %u", opcode);
257 err = cmd_status(sk, opcode, 0x01);
271 static int mgmt_event(u16 event, void *data, u16 data_len)
274 struct mgmt_hdr *hdr;
276 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
280 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
282 hdr = (void *) skb_put(skb, sizeof(*hdr));
283 hdr->opcode = cpu_to_le16(event);
284 hdr->len = cpu_to_le16(data_len);
286 memcpy(skb_put(skb, data_len), data, data_len);
288 hci_send_to_sock(NULL, skb);
294 int mgmt_index_added(u16 index)
296 struct mgmt_ev_index_added ev;
298 put_unaligned_le16(index, &ev.index);
300 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev));
303 int mgmt_index_removed(u16 index)
305 struct mgmt_ev_index_added ev;
307 put_unaligned_le16(index, &ev.index);
309 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev));