blob: d479e241a9de38652bfcbf51bdeb2ae64b8f5359 [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 Hedbergf7b64e692010-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);
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200132
133 hci_del_off_timer(d);
134
135 if (test_bit(HCI_SETUP, &d->flags))
136 continue;
137
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200138 put_unaligned_le16(d->id, &rp->index[i++]);
139 BT_DBG("Added hci%u", d->id);
140 }
141
142 read_unlock(&hci_dev_list_lock);
143
144 if (sock_queue_rcv_skb(sk, skb) < 0)
145 kfree_skb(skb);
146
147 return 0;
148}
149
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200150static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200151{
152 struct sk_buff *skb;
153 struct mgmt_hdr *hdr;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200154 struct mgmt_ev_cmd_complete *ev;
155 struct mgmt_rp_read_info *rp;
156 struct mgmt_cp_read_info *cp;
157 struct hci_dev *hdev;
158 u16 dev_id;
Johan Hedberg03811012010-12-08 00:21:06 +0200159
160 BT_DBG("sock %p", sk);
161
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200162 if (len != 2)
163 return cmd_status(sk, MGMT_OP_READ_INFO, EINVAL);
164
165 skb = alloc_skb(sizeof(*hdr) + sizeof(*ev) + sizeof(*rp), GFP_ATOMIC);
Johan Hedberg03811012010-12-08 00:21:06 +0200166 if (!skb)
Johan Hedberge41d8b42010-12-13 21:07:03 +0200167 return -ENOMEM;
Johan Hedberg03811012010-12-08 00:21:06 +0200168
169 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200170 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
171 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200172
173 ev = (void *) skb_put(skb, sizeof(*ev));
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200174 put_unaligned_le16(MGMT_OP_READ_INFO, &ev->opcode);
175
176 rp = (void *) skb_put(skb, sizeof(*rp));
177
178 cp = (void *) data;
179 dev_id = get_unaligned_le16(&cp->index);
180
181 BT_DBG("request for hci%u", dev_id);
182
183 hdev = hci_dev_get(dev_id);
184 if (!hdev) {
185 kfree_skb(skb);
186 return cmd_status(sk, MGMT_OP_READ_INFO, ENODEV);
187 }
188
Johan Hedbergab81cbf2010-12-15 13:53:18 +0200189 hci_del_off_timer(hdev);
190
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200191 hci_dev_lock_bh(hdev);
192
193 put_unaligned_le16(hdev->id, &rp->index);
194 rp->type = hdev->dev_type;
195
196 rp->powered = test_bit(HCI_UP, &hdev->flags);
197 rp->discoverable = test_bit(HCI_ISCAN, &hdev->flags);
198 rp->pairable = test_bit(HCI_PSCAN, &hdev->flags);
199
200 if (test_bit(HCI_AUTH, &hdev->flags))
201 rp->sec_mode = 3;
202 else if (hdev->ssp_mode > 0)
203 rp->sec_mode = 4;
204 else
205 rp->sec_mode = 2;
206
207 bacpy(&rp->bdaddr, &hdev->bdaddr);
208 memcpy(rp->features, hdev->features, 8);
209 memcpy(rp->dev_class, hdev->dev_class, 3);
210 put_unaligned_le16(hdev->manufacturer, &rp->manufacturer);
211 rp->hci_ver = hdev->hci_ver;
212 put_unaligned_le16(hdev->hci_rev, &rp->hci_rev);
213
214 hci_dev_unlock_bh(hdev);
215 hci_dev_put(hdev);
Johan Hedberg03811012010-12-08 00:21:06 +0200216
217 if (sock_queue_rcv_skb(sk, skb) < 0)
218 kfree_skb(skb);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200219
220 return 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200221}
222
223int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen)
224{
225 unsigned char *buf;
226 struct mgmt_hdr *hdr;
227 u16 opcode, len;
228 int err;
229
230 BT_DBG("got %zu bytes", msglen);
231
232 if (msglen < sizeof(*hdr))
233 return -EINVAL;
234
235 buf = kmalloc(msglen, GFP_ATOMIC);
236 if (!buf)
237 return -ENOMEM;
238
239 if (memcpy_fromiovec(buf, msg->msg_iov, msglen)) {
240 err = -EFAULT;
241 goto done;
242 }
243
244 hdr = (struct mgmt_hdr *) buf;
245 opcode = get_unaligned_le16(&hdr->opcode);
246 len = get_unaligned_le16(&hdr->len);
247
248 if (len != msglen - sizeof(*hdr)) {
249 err = -EINVAL;
250 goto done;
251 }
252
253 switch (opcode) {
Johan Hedberg02d98122010-12-13 21:07:04 +0200254 case MGMT_OP_READ_VERSION:
255 err = read_version(sk);
256 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200257 case MGMT_OP_READ_INDEX_LIST:
258 err = read_index_list(sk);
259 break;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200260 case MGMT_OP_READ_INFO:
261 err = read_controller_info(sk, buf + sizeof(*hdr), len);
262 break;
Johan Hedberg03811012010-12-08 00:21:06 +0200263 default:
264 BT_DBG("Unknown op %u", opcode);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200265 err = cmd_status(sk, opcode, 0x01);
Johan Hedberg03811012010-12-08 00:21:06 +0200266 break;
267 }
268
Johan Hedberge41d8b42010-12-13 21:07:03 +0200269 if (err < 0)
270 goto done;
271
Johan Hedberg03811012010-12-08 00:21:06 +0200272 err = msglen;
273
274done:
275 kfree(buf);
276 return err;
277}
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200278
279static int mgmt_event(u16 event, void *data, u16 data_len)
280{
281 struct sk_buff *skb;
282 struct mgmt_hdr *hdr;
283
284 skb = alloc_skb(sizeof(*hdr) + data_len, GFP_ATOMIC);
285 if (!skb)
286 return -ENOMEM;
287
288 bt_cb(skb)->channel = HCI_CHANNEL_CONTROL;
289
290 hdr = (void *) skb_put(skb, sizeof(*hdr));
291 hdr->opcode = cpu_to_le16(event);
292 hdr->len = cpu_to_le16(data_len);
293
294 memcpy(skb_put(skb, data_len), data, data_len);
295
296 hci_send_to_sock(NULL, skb);
297 kfree_skb(skb);
298
299 return 0;
300}
301
302int mgmt_index_added(u16 index)
303{
304 struct mgmt_ev_index_added ev;
305
306 put_unaligned_le16(index, &ev.index);
307
308 return mgmt_event(MGMT_EV_INDEX_ADDED, &ev, sizeof(ev));
309}
310
311int mgmt_index_removed(u16 index)
312{
313 struct mgmt_ev_index_added ev;
314
315 put_unaligned_le16(index, &ev.index);
316
317 return mgmt_event(MGMT_EV_INDEX_REMOVED, &ev, sizeof(ev));
318}