blob: f827fd9083808f9126148cd8541fd29ad552b81b [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);
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
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200142static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
Johan Hedberg03811012010-12-08 00:21:06 +0200143{
144 struct sk_buff *skb;
145 struct mgmt_hdr *hdr;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200146 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;
Johan Hedberg03811012010-12-08 00:21:06 +0200151
152 BT_DBG("sock %p", sk);
153
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200154 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);
Johan Hedberg03811012010-12-08 00:21:06 +0200158 if (!skb)
Johan Hedberge41d8b42010-12-13 21:07:03 +0200159 return -ENOMEM;
Johan Hedberg03811012010-12-08 00:21:06 +0200160
161 hdr = (void *) skb_put(skb, sizeof(*hdr));
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200162 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
163 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
Johan Hedberg03811012010-12-08 00:21:06 +0200164
165 ev = (void *) skb_put(skb, sizeof(*ev));
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200166 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);
Johan Hedberg03811012010-12-08 00:21:06 +0200206
207 if (sock_queue_rcv_skb(sk, skb) < 0)
208 kfree_skb(skb);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200209
210 return 0;
Johan Hedberg03811012010-12-08 00:21:06 +0200211}
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) {
Johan Hedberg02d98122010-12-13 21:07:04 +0200244 case MGMT_OP_READ_VERSION:
245 err = read_version(sk);
246 break;
Johan Hedbergfaba42e2010-12-13 21:07:05 +0200247 case MGMT_OP_READ_INDEX_LIST:
248 err = read_index_list(sk);
249 break;
Johan Hedbergf7b64e692010-12-13 21:07:06 +0200250 case MGMT_OP_READ_INFO:
251 err = read_controller_info(sk, buf + sizeof(*hdr), len);
252 break;
Johan Hedberg03811012010-12-08 00:21:06 +0200253 default:
254 BT_DBG("Unknown op %u", opcode);
Johan Hedberge41d8b42010-12-13 21:07:03 +0200255 err = cmd_status(sk, opcode, 0x01);
Johan Hedberg03811012010-12-08 00:21:06 +0200256 break;
257 }
258
Johan Hedberge41d8b42010-12-13 21:07:03 +0200259 if (err < 0)
260 goto done;
261
Johan Hedberg03811012010-12-08 00:21:06 +0200262 err = msglen;
263
264done:
265 kfree(buf);
266 return err;
267}
Johan Hedbergc71e97b2010-12-13 21:07:07 +0200268
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}