Bluetooth: Fix failure to release lock in read_index_list()
[deliverable/linux.git] / net / bluetooth / mgmt.c
CommitLineData
0381101f
JH
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
02d98129
JH
32#define MGMT_VERSION 0
33#define MGMT_REVISION 1
34
f7b64e69
JH
35static 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
02d98129
JH
62static 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
faba42eb
JH
92static 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);
b2c60d42
JJ
114 if (!skb) {
115 read_unlock(&hci_dev_list_lock);
faba42eb 116 return -ENOMEM;
b2c60d42 117 }
faba42eb
JH
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 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
f7b64e69 144static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
0381101f
JH
145{
146 struct sk_buff *skb;
147 struct mgmt_hdr *hdr;
f7b64e69
JH
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;
152 u16 dev_id;
0381101f
JH
153
154 BT_DBG("sock %p", sk);
155
f7b64e69
JH
156 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);
0381101f 160 if (!skb)
e41d8b4e 161 return -ENOMEM;
0381101f
JH
162
163 hdr = (void *) skb_put(skb, sizeof(*hdr));
f7b64e69
JH
164 hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
165 hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
0381101f
JH
166
167 ev = (void *) skb_put(skb, sizeof(*ev));
f7b64e69
JH
168 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);
0381101f
JH
208
209 if (sock_queue_rcv_skb(sk, skb) < 0)
210 kfree_skb(skb);
e41d8b4e
JH
211
212 return 0;
0381101f
JH
213}
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) {
02d98129
JH
246 case MGMT_OP_READ_VERSION:
247 err = read_version(sk);
248 break;
faba42eb
JH
249 case MGMT_OP_READ_INDEX_LIST:
250 err = read_index_list(sk);
251 break;
f7b64e69
JH
252 case MGMT_OP_READ_INFO:
253 err = read_controller_info(sk, buf + sizeof(*hdr), len);
254 break;
0381101f
JH
255 default:
256 BT_DBG("Unknown op %u", opcode);
e41d8b4e 257 err = cmd_status(sk, opcode, 0x01);
0381101f
JH
258 break;
259 }
260
e41d8b4e
JH
261 if (err < 0)
262 goto done;
263
0381101f
JH
264 err = msglen;
265
266done:
267 kfree(buf);
268 return err;
269}
c71e97bf
JH
270
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}
This page took 0.050868 seconds and 5 git commands to generate.