xref: /openbmc/linux/net/bluetooth/mgmt.c (revision df2634f43f5106947f3735a0b61a6527a4b278cd)
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 		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 
142 static int read_controller_info(struct sock *sk, unsigned char *data, u16 len)
143 {
144 	struct sk_buff *skb;
145 	struct mgmt_hdr *hdr;
146 	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;
151 
152 	BT_DBG("sock %p", sk);
153 
154 	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);
158 	if (!skb)
159 		return -ENOMEM;
160 
161 	hdr = (void *) skb_put(skb, sizeof(*hdr));
162 	hdr->opcode = cpu_to_le16(MGMT_EV_CMD_COMPLETE);
163 	hdr->len = cpu_to_le16(sizeof(*ev) + sizeof(*rp));
164 
165 	ev = (void *) skb_put(skb, sizeof(*ev));
166 	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);
206 
207 	if (sock_queue_rcv_skb(sk, skb) < 0)
208 		kfree_skb(skb);
209 
210 	return 0;
211 }
212 
213 int 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) {
244 	case MGMT_OP_READ_VERSION:
245 		err = read_version(sk);
246 		break;
247 	case MGMT_OP_READ_INDEX_LIST:
248 		err = read_index_list(sk);
249 		break;
250 	case MGMT_OP_READ_INFO:
251 		err = read_controller_info(sk, buf + sizeof(*hdr), len);
252 		break;
253 	default:
254 		BT_DBG("Unknown op %u", opcode);
255 		err = cmd_status(sk, opcode, 0x01);
256 		break;
257 	}
258 
259 	if (err < 0)
260 		goto done;
261 
262 	err = msglen;
263 
264 done:
265 	kfree(buf);
266 	return err;
267 }
268 
269 static 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 
292 int 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 
301 int 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 }
309