f746e19ebec0d795636a5639c465e36fc67bdf28
[linux-2.6.git] / net / bluetooth / mgmt.c
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
35 static 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
62 static 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
92 static 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                 read_unlock(&hci_dev_list_lock);
116                 return -ENOMEM;
117         }
118
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
133                 hci_del_off_timer(d);
134
135                 if (test_bit(HCI_SETUP, &d->flags))
136                         continue;
137
138                 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
150 static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
151 {
152         struct sk_buff *skb;
153         struct mgmt_hdr *hdr;
154         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;
159
160         BT_DBG("sock %p", sk);
161
162         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);
166         if (!skb)
167                 return -ENOMEM;
168
169         hdr = (void *) skb_put(skb, sizeof(*hdr));
170         hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
171         hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
172
173         ev = (void *) skb_put(skb, sizeof(*ev));
174         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
189         hci_del_off_timer(hdev);
190
191         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);
216
217         if (sock_queue_rcv_skb(sk, skb) < 0)
218                 kfree_skb(skb);
219
220         return 0;
221 }
222
223 int 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) {
254         case MGMT_OP_READ_VERSION:
255                 err = read_version(sk);
256                 break;
257         case MGMT_OP_READ_INDEX_LIST:
258                 err = read_index_list(sk);
259                 break;
260         case MGMT_OP_READ_INFO:
261                 err = read_controller_info(sk, buf + sizeof(*hdr), len);
262                 break;
263         default:
264                 BT_DBG("Unknown op %u", opcode);
265                 err = cmd_status(sk, opcode, 0x01);
266                 break;
267         }
268
269         if (err < 0)
270                 goto done;
271
272         err = msglen;
273
274 done:
275         kfree(buf);
276         return err;
277 }
278
279 static 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
302 int 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
311 int 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 }
319
320 int mgmt_powered(u16 index, u8 powered)
321 {
322         struct mgmt_ev_powered ev;
323
324         put_unaligned_le16(index, &ev.index);
325         ev.powered = powered;
326
327         return mgmt_event(MGMT_EV_POWERED, &ev, sizeof(ev));
328 }