blob: ace872615c06c610a671a62a857e0d6ea760543f [file] [log] [blame]
Johan Hedberg03811012010-12-08 00:21:06 +02001/*
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
Johan Hedberg02d98122010-12-13 21:07:04 +020032#define MGMT_VERSION 0
33#define MGMT_REVISION 1
34
Johan Hedbergf7b64e62010-12-13 21:07:06 +020035static 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
Johan Hedberg02d98122010-12-13 21:07:04 +020062static 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
Johan Hedbergfaba42e2010-12-13 21:07:05 +020092static 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);
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100114 if (!skb) {
115 read_unlock(&hci_dev_list_lock);
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200116 return -ENOMEM;
Jesper Juhlb2c60d42011-01-14 00:18:49 +0100117 }
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200118
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);
122
123 ev = (void *) skb_put(skb, sizeof(*ev));
124 put_unaligned_le16(MGMT_OP_READ_INDEX_LIST, &ev->opcode);
125
126 rp = (void *) skb_put(skb, sizeof(*rp) + (2 * count));
127 put_unaligned_le16(count, &rp->num_controllers);
128
129 i = 0;
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);
134 }
135
136 read_unlock(&hci_dev_list_lock);
137
138 if (sock_queue_rcv_skb(sk, skb) < 0)
139 kfree_skb(skb);
140
141 return 0;
142}
143
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200144static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200145{
146 struct sk_buff *skb;
147 struct mgmt_hdr *hdr;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200148 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;
152 u16 dev_id;
Johan Hedberg03811012010-12-08 00:21:06 +0200153
154 BT_DBG("sock %p", sk);
155
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200156 if (len != 2)
157 return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
158
159 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
Johan Hedberg03811012010-12-08 00:21:06 +0200160 if (!skb)
Johan Hedberge41d8b42010-12-13 21:07:03 +0200161 return -ENOMEM;
Johan Hedberg03811012010-12-08 00:21:06 +0200162
163 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200164 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
165 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200166
167 ev = (void *) skb_put(skb, sizeof(*ev));
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200168 put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
169
170 rp = (void *) skb_put(skb, sizeof(*rp));
171
172 cp = (void *) data;
173 dev_id = get_unaligned_le16(&cp->index);
174
175 BT_DBG("request for hci%u", dev_id);
176
177 hdev = hci_dev_get(dev_id);
178 if (!hdev) {
179 kfree_skb(skb);
180 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
181 }
182
183 hci_dev_lock_bh(hdev);
184
185 put_unaligned_le16(hdev->id, &rp->index);
186 rp->type = hdev->dev_type;
187
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);
191
192 if (test_bit(HCI_AUTH, &hdev->flags))
193 rp->sec_mode = 3;
194 else if (hdev->ssp_mode > 0)
195 rp->sec_mode = 4;
196 else
197 rp->sec_mode = 2;
198
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);
205
206 hci_dev_unlock_bh(hdev);
207 hci_dev_put(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200208
209 if (sock_queue_rcv_skb(sk, skb) < 0)
210 kfree_skb(skb);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200211
212 return 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200213}
214
215int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
216{
217 unsigned char *buf;
218 struct mgmt_hdr *hdr;
219 u16 opcode, len;
220 int err;
221
222 BT_DBG("got %zu bytes", msglen);
223
224 if (msglen < sizeof(*hdr))
225 return -EINVAL;
226
227 buf = kmalloc(msglen, GFP_ATOMIC);
228 if (!buf)
229 return -ENOMEM;
230
231 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
232 err = -EFAULT;
233 goto done;
234 }
235
236 hdr = (struct mgmt_hdr *) buf;
237 opcode = get_unaligned_le16(&hdr->opcode);
238 len = get_unaligned_le16(&hdr->len);
239
240 if (len != msglen - sizeof(*hdr)) {
241 err = -EINVAL;
242 goto done;
243 }
244
245 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +0200246 case MGMT_OP_READ_VERSION:
247 err = read_version(sk);
248 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200249 case MGMT_OP_READ_INDEX_LIST:
250 err = read_index_list(sk);
251 break;
Johan Hedbergf7b64e62010-12-13 21:07:06 +0200252 case MGMT_OP_READ_INFO:
253 err = read_controller_info(sk, buf + sizeof(*hdr), len);
254 break;
Johan Hedberg03811012010-12-08 00:21:06 +0200255 default:
256 BT_DBG("Unknown op %u", opcode);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200257 err = cmd_status(sk, opcode, 0x01);
Johan Hedberg03811012010-12-08 00:21:06 +0200258 break;
259 }
260
Johan Hedberge41d8b42010-12-13 21:07:03 +0200261 if (err < 0)
262 goto done;
263
Johan Hedberg03811012010-12-08 00:21:06 +0200264 err = msglen;
265
266done:
267 kfree(buf);
268 return err;
269}
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200270
271static int mgmt_event(u16 event, void *data, u16 data_len)
272{
273 struct sk_buff *skb;
274 struct mgmt_hdr *hdr;
275
276 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
277 if (!skb)
278 return -ENOMEM;
279
280 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
281
282 hdr = (void *) skb_put(skb, sizeof(*hdr));
283 hdr->opcode = cpu_to_le16(event);
284 hdr->len = cpu_to_le16(data_len);
285
286 memcpy(skb_put(skb, data_len), data, data_len);
287
288 hci_send_to_sock(NULL, skb);
289 kfree_skb(skb);
290
291 return 0;
292}
293
294int mgmt_index_added(u16 index)
295{
296 struct mgmt_ev_index_added ev;
297
298 put_unaligned_le16(index, &ev.index);
299
300 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev));
301}
302
303int mgmt_index_removed(u16 index)
304{
305 struct mgmt_ev_index_added ev;
306
307 put_unaligned_le16(index, &ev.index);
308
309 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev));
310}