xref: /netbsd-src/lib/libbluetooth/bt_dev.c (revision 37a23ecf39b616ebc8f7f8be96b814ad1bd168f5)
1*37a23ecfSmsaitoh /*	$NetBSD: bt_dev.c,v 1.5 2023/06/24 05:18:12 msaitoh Exp $	*/
2bd42c2e8Splunky 
3bd42c2e8Splunky /*-
4bd42c2e8Splunky  * Copyright (c) 2009 Iain Hibbert
5bd42c2e8Splunky  * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
6bd42c2e8Splunky  * All rights reserved.
7bd42c2e8Splunky  *
8bd42c2e8Splunky  * Redistribution and use in source and binary forms, with or without
9bd42c2e8Splunky  * modification, are permitted provided that the following conditions
10bd42c2e8Splunky  * are met:
11bd42c2e8Splunky  * 1. Redistributions of source code must retain the above copyright
12bd42c2e8Splunky  *    notice, this list of conditions and the following disclaimer.
13bd42c2e8Splunky  * 2. Redistributions in binary form must reproduce the above copyright
14bd42c2e8Splunky  *    notice, this list of conditions and the following disclaimer in the
15bd42c2e8Splunky  *    documentation and/or other materials provided with the distribution.
16bd42c2e8Splunky  *
17bd42c2e8Splunky  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18bd42c2e8Splunky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19bd42c2e8Splunky  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20bd42c2e8Splunky  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21bd42c2e8Splunky  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22bd42c2e8Splunky  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23bd42c2e8Splunky  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24bd42c2e8Splunky  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25bd42c2e8Splunky  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26bd42c2e8Splunky  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27bd42c2e8Splunky  * SUCH DAMAGE.
28bd42c2e8Splunky  */
29bd42c2e8Splunky 
30bd42c2e8Splunky /*-
31bd42c2e8Splunky  * Copyright (c) 2006 Itronix Inc.
32bd42c2e8Splunky  * All rights reserved.
33bd42c2e8Splunky  *
34bd42c2e8Splunky  * Written by Iain Hibbert for Itronix Inc.
35bd42c2e8Splunky  *
36bd42c2e8Splunky  * Redistribution and use in source and binary forms, with or without
37bd42c2e8Splunky  * modification, are permitted provided that the following conditions
38bd42c2e8Splunky  * are met:
39bd42c2e8Splunky  * 1. Redistributions of source code must retain the above copyright
40bd42c2e8Splunky  *    notice, this list of conditions and the following disclaimer.
41bd42c2e8Splunky  * 2. Redistributions in binary form must reproduce the above copyright
42bd42c2e8Splunky  *    notice, this list of conditions and the following disclaimer in the
43bd42c2e8Splunky  *    documentation and/or other materials provided with the distribution.
44bd42c2e8Splunky  * 3. The name of Itronix Inc. may not be used to endorse
45bd42c2e8Splunky  *    or promote products derived from this software without specific
46bd42c2e8Splunky  *    prior written permission.
47bd42c2e8Splunky  *
48bd42c2e8Splunky  * THIS SOFTWARE IS PROVIDED BY ITRONIX INC. ``AS IS'' AND
49bd42c2e8Splunky  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
50bd42c2e8Splunky  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
51bd42c2e8Splunky  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL ITRONIX INC. BE LIABLE FOR ANY
52bd42c2e8Splunky  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
53bd42c2e8Splunky  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
54bd42c2e8Splunky  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
55bd42c2e8Splunky  * ON ANY THEORY OF LIABILITY, WHETHER IN
56bd42c2e8Splunky  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
57bd42c2e8Splunky  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
58bd42c2e8Splunky  * POSSIBILITY OF SUCH DAMAGE.
59bd42c2e8Splunky  */
60bd42c2e8Splunky 
61bd42c2e8Splunky #include <sys/cdefs.h>
62*37a23ecfSmsaitoh __RCSID("$NetBSD: bt_dev.c,v 1.5 2023/06/24 05:18:12 msaitoh Exp $");
63bd42c2e8Splunky 
64bd42c2e8Splunky #include <sys/event.h>
65bd42c2e8Splunky #include <sys/ioctl.h>
66bd42c2e8Splunky #include <sys/param.h>
67bd42c2e8Splunky #include <sys/time.h>
68bd42c2e8Splunky #include <sys/uio.h>
69bd42c2e8Splunky 
70bd42c2e8Splunky #include <bluetooth.h>
71bd42c2e8Splunky #include <errno.h>
72bd42c2e8Splunky #include <stdlib.h>
73bd42c2e8Splunky #include <string.h>
74bd42c2e8Splunky #include <unistd.h>
75bd42c2e8Splunky 
76bd42c2e8Splunky int
bt_devaddr(const char * name,bdaddr_t * addr)77bd42c2e8Splunky bt_devaddr(const char *name, bdaddr_t *addr)
78bd42c2e8Splunky {
79bd42c2e8Splunky 	struct btreq btr;
80bd42c2e8Splunky 	bdaddr_t bdaddr;
81bd42c2e8Splunky 	int s, rv;
82bd42c2e8Splunky 
83bd42c2e8Splunky 	if (name == NULL) {
84bd42c2e8Splunky 		errno = EINVAL;
85bd42c2e8Splunky 		return 0;
86bd42c2e8Splunky 	}
87bd42c2e8Splunky 
88bd42c2e8Splunky 	if (addr == NULL)
89bd42c2e8Splunky 		addr = &bdaddr;
90bd42c2e8Splunky 
91bd42c2e8Splunky 	if (bt_aton(name, addr))
92bd42c2e8Splunky 		return bt_devname(NULL, addr);
93bd42c2e8Splunky 
94bd42c2e8Splunky 	memset(&btr, 0, sizeof(btr));
95ae8d4ecfSchristos 	strlcpy(btr.btr_name, name, HCI_DEVNAME_SIZE);
96bd42c2e8Splunky 
97bd42c2e8Splunky 	s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
98bd42c2e8Splunky 	if (s == -1)
99bd42c2e8Splunky 		return 0;
100bd42c2e8Splunky 
101bd42c2e8Splunky 	rv = ioctl(s, SIOCGBTINFO, &btr);
102bd42c2e8Splunky 	close(s);
103bd42c2e8Splunky 
104bd42c2e8Splunky 	if (rv == -1)
105bd42c2e8Splunky 		return 0;
106bd42c2e8Splunky 
107bd42c2e8Splunky 	if ((btr.btr_flags & BTF_UP) == 0) {
108bd42c2e8Splunky 		errno = ENXIO;
109bd42c2e8Splunky 		return 0;
110bd42c2e8Splunky 	}
111bd42c2e8Splunky 
112bd42c2e8Splunky 	bdaddr_copy(addr, &btr.btr_bdaddr);
113bd42c2e8Splunky 	return 1;
114bd42c2e8Splunky }
115bd42c2e8Splunky 
116bd42c2e8Splunky int
bt_devname(char * name,const bdaddr_t * bdaddr)117bd42c2e8Splunky bt_devname(char *name, const bdaddr_t *bdaddr)
118bd42c2e8Splunky {
119bd42c2e8Splunky 	struct btreq btr;
120bd42c2e8Splunky 	int s, rv;
121bd42c2e8Splunky 
122bd42c2e8Splunky 	if (bdaddr == NULL) {
123bd42c2e8Splunky 		errno = EINVAL;
124bd42c2e8Splunky 		return 0;
125bd42c2e8Splunky 	}
126bd42c2e8Splunky 
127bd42c2e8Splunky 	memset(&btr, 0, sizeof(btr));
128bd42c2e8Splunky 	bdaddr_copy(&btr.btr_bdaddr, bdaddr);
129bd42c2e8Splunky 
130bd42c2e8Splunky 	s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
131bd42c2e8Splunky 	if (s == -1)
132bd42c2e8Splunky 		return 0;
133bd42c2e8Splunky 
134bd42c2e8Splunky 	rv = ioctl(s, SIOCGBTINFOA, &btr);
135bd42c2e8Splunky 	close(s);
136bd42c2e8Splunky 
137bd42c2e8Splunky 	if (rv == -1)
138bd42c2e8Splunky 		return 0;
139bd42c2e8Splunky 
140bd42c2e8Splunky 	if ((btr.btr_flags & BTF_UP) == 0) {
141bd42c2e8Splunky 		errno = ENXIO;
142bd42c2e8Splunky 		return 0;
143bd42c2e8Splunky 	}
144bd42c2e8Splunky 
145bd42c2e8Splunky 	if (name != NULL)
146bd42c2e8Splunky 		strlcpy(name, btr.btr_name, HCI_DEVNAME_SIZE);
147bd42c2e8Splunky 
148bd42c2e8Splunky 	return 1;
149bd42c2e8Splunky }
150bd42c2e8Splunky 
151bd42c2e8Splunky int
bt_devopen(const char * name,int options)152bd42c2e8Splunky bt_devopen(const char *name, int options)
153bd42c2e8Splunky {
154bd42c2e8Splunky 	struct sockaddr_bt	sa;
155bd42c2e8Splunky 	int			opt, s;
156bd42c2e8Splunky 
157bd42c2e8Splunky 	memset(&sa, 0, sizeof(sa));
158bd42c2e8Splunky 	sa.bt_len = sizeof(sa);
159bd42c2e8Splunky 	sa.bt_family = AF_BLUETOOTH;
160bd42c2e8Splunky 
161bd42c2e8Splunky 	if (name != NULL && !bt_devaddr(name, &sa.bt_bdaddr))
162bd42c2e8Splunky 		return -1;
163bd42c2e8Splunky 
164bd42c2e8Splunky 	s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
165bd42c2e8Splunky 	if (s == -1)
166bd42c2e8Splunky 		return -1;
167bd42c2e8Splunky 
168bd42c2e8Splunky 	opt = 1;
169bd42c2e8Splunky 
170bd42c2e8Splunky 	if ((options & BTOPT_DIRECTION) && setsockopt(s, BTPROTO_HCI,
171bd42c2e8Splunky 	    SO_HCI_DIRECTION, &opt, sizeof(opt)) == -1) {
172bd42c2e8Splunky 		close(s);
173bd42c2e8Splunky 		return -1;
174bd42c2e8Splunky 	}
175bd42c2e8Splunky 
176bd42c2e8Splunky 	if ((options & BTOPT_TIMESTAMP) && setsockopt(s, SOL_SOCKET,
177bd42c2e8Splunky 	    SO_TIMESTAMP, &opt, sizeof(opt)) == -1) {
178bd42c2e8Splunky 		close(s);
179bd42c2e8Splunky 		return -1;
180bd42c2e8Splunky 	}
181bd42c2e8Splunky 
182bd42c2e8Splunky 	if (bind(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
183bd42c2e8Splunky 		close(s);
184bd42c2e8Splunky 		return -1;
185bd42c2e8Splunky 	}
186bd42c2e8Splunky 
187bd42c2e8Splunky 	if (name != NULL
188bd42c2e8Splunky 	    && connect(s, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
189bd42c2e8Splunky 		close(s);
190bd42c2e8Splunky 		return -1;
191bd42c2e8Splunky 	}
192bd42c2e8Splunky 
193bd42c2e8Splunky 	return s;
194bd42c2e8Splunky }
195bd42c2e8Splunky 
196bd42c2e8Splunky ssize_t
bt_devsend(int s,uint16_t opcode,void * param,size_t plen)197bd42c2e8Splunky bt_devsend(int s, uint16_t opcode, void *param, size_t plen)
198bd42c2e8Splunky {
199bd42c2e8Splunky 	hci_cmd_hdr_t	hdr;
200bd42c2e8Splunky 	struct iovec	iov[2];
201bd42c2e8Splunky 	ssize_t		n;
202bd42c2e8Splunky 
203bd42c2e8Splunky 	if (plen > UINT8_MAX
204bd42c2e8Splunky 	    || (plen == 0 && param != NULL)
205bd42c2e8Splunky 	    || (plen != 0 && param == NULL)) {
206bd42c2e8Splunky 		errno = EINVAL;
207bd42c2e8Splunky 		return -1;
208bd42c2e8Splunky 	}
209bd42c2e8Splunky 
210bd42c2e8Splunky 	hdr.type = HCI_CMD_PKT;
211bd42c2e8Splunky 	hdr.opcode = htole16(opcode);
212bd42c2e8Splunky 	hdr.length = (uint8_t)plen;
213bd42c2e8Splunky 
214bd42c2e8Splunky 	iov[0].iov_base = &hdr;
215bd42c2e8Splunky 	iov[0].iov_len = sizeof(hdr);
216bd42c2e8Splunky 
217bd42c2e8Splunky 	iov[1].iov_base = param;
218bd42c2e8Splunky 	iov[1].iov_len = plen;
219bd42c2e8Splunky 
220bd42c2e8Splunky 	while ((n = writev(s, iov, __arraycount(iov))) == -1) {
221bd42c2e8Splunky 		if (errno == EINTR)
222bd42c2e8Splunky 			continue;
223bd42c2e8Splunky 
224bd42c2e8Splunky 		return -1;
225bd42c2e8Splunky 	}
226bd42c2e8Splunky 
227bd42c2e8Splunky 	return n;
228bd42c2e8Splunky }
229bd42c2e8Splunky 
230bd42c2e8Splunky ssize_t
bt_devrecv(int s,void * buf,size_t size,time_t to)231bd42c2e8Splunky bt_devrecv(int s, void *buf, size_t size, time_t to)
232bd42c2e8Splunky {
233bd42c2e8Splunky 	struct kevent	ev;
234bd42c2e8Splunky 	struct timespec ts;
235bd42c2e8Splunky 	uint8_t		*p;
236bd42c2e8Splunky 	ssize_t		n;
237bd42c2e8Splunky 	int		kq;
238bd42c2e8Splunky 
239bd42c2e8Splunky 	if (buf == NULL || size == 0) {
240bd42c2e8Splunky 		errno = EINVAL;
241bd42c2e8Splunky 		return -1;
242bd42c2e8Splunky 	}
243bd42c2e8Splunky 
244bd42c2e8Splunky 	if (to >= 0) {	/* timeout is optional */
245bd42c2e8Splunky 		kq = kqueue();
246bd42c2e8Splunky 		if (kq == -1)
247bd42c2e8Splunky 			return -1;
248bd42c2e8Splunky 
249bd42c2e8Splunky 		EV_SET(&ev, s, EVFILT_READ, EV_ADD, 0, 0, 0);
250bd42c2e8Splunky 
251bd42c2e8Splunky 		ts.tv_sec = to;
252bd42c2e8Splunky 		ts.tv_nsec = 0;
253bd42c2e8Splunky 
254bd42c2e8Splunky 		while (kevent(kq, &ev, 1, &ev, 1, &ts) == -1) {
255bd42c2e8Splunky 			if (errno == EINTR)
256bd42c2e8Splunky 				continue;
257bd42c2e8Splunky 
258bd42c2e8Splunky 			close(kq);
259bd42c2e8Splunky 			return -1;
260bd42c2e8Splunky 		}
261bd42c2e8Splunky 
262bd42c2e8Splunky 		close(kq);
263bd42c2e8Splunky 
264bd42c2e8Splunky 		if (ev.data == 0) {
265bd42c2e8Splunky 			errno = ETIMEDOUT;
266bd42c2e8Splunky 			return -1;
267bd42c2e8Splunky 		}
268bd42c2e8Splunky 	}
269bd42c2e8Splunky 
270bd42c2e8Splunky 	while ((n = recv(s, buf, size, 0)) == -1) {
271bd42c2e8Splunky 		if (errno == EINTR)
272bd42c2e8Splunky 			continue;
273bd42c2e8Splunky 
274bd42c2e8Splunky 		return -1;
275bd42c2e8Splunky 	}
276bd42c2e8Splunky 
277bd42c2e8Splunky 	if (n == 0)
278bd42c2e8Splunky 		return 0;
279bd42c2e8Splunky 
280bd42c2e8Splunky 	p = buf;
281bd42c2e8Splunky 	switch (p[0]) {	/* validate that they get complete packets */
282bd42c2e8Splunky 	case HCI_CMD_PKT:
283bd42c2e8Splunky 		if (sizeof(hci_cmd_hdr_t) > (size_t)n
284bd42c2e8Splunky 		    || sizeof(hci_cmd_hdr_t) + p[3] != (size_t)n)
285bd42c2e8Splunky 			break;
286bd42c2e8Splunky 
287bd42c2e8Splunky 		return n;
288bd42c2e8Splunky 
289bd42c2e8Splunky 	case HCI_ACL_DATA_PKT:
290bd42c2e8Splunky 		if (sizeof(hci_acldata_hdr_t) > (size_t)n
291bd42c2e8Splunky 		    || sizeof(hci_acldata_hdr_t) + le16dec(p + 3) != (size_t)n)
292bd42c2e8Splunky 			break;
293bd42c2e8Splunky 
294bd42c2e8Splunky 		return n;
295bd42c2e8Splunky 
296bd42c2e8Splunky 	case HCI_SCO_DATA_PKT:
297bd42c2e8Splunky 		if (sizeof(hci_scodata_hdr_t) > (size_t)n
298bd42c2e8Splunky 		    || sizeof(hci_scodata_hdr_t) + p[3] != (size_t)n)
299bd42c2e8Splunky 			break;
300bd42c2e8Splunky 
301bd42c2e8Splunky 		return n;
302bd42c2e8Splunky 
303bd42c2e8Splunky 	case HCI_EVENT_PKT:
304bd42c2e8Splunky 		if (sizeof(hci_event_hdr_t) > (size_t)n
305bd42c2e8Splunky 		    || sizeof(hci_event_hdr_t) + p[2] != (size_t)n)
306bd42c2e8Splunky 			break;
307bd42c2e8Splunky 
308bd42c2e8Splunky 		return n;
309bd42c2e8Splunky 
310bd42c2e8Splunky 	default:
311bd42c2e8Splunky 		break;
312bd42c2e8Splunky 	}
313bd42c2e8Splunky 
314bd42c2e8Splunky 	errno = EIO;
315bd42c2e8Splunky 	return -1;
316bd42c2e8Splunky }
317bd42c2e8Splunky 
318bd42c2e8Splunky /*
319bd42c2e8Splunky  * Internal handler for bt_devreq(), do the actual request.
320bd42c2e8Splunky  */
321bd42c2e8Splunky static int
bt__devreq(int s,struct bt_devreq * req,time_t t_end)322bd42c2e8Splunky bt__devreq(int s, struct bt_devreq *req, time_t t_end)
323bd42c2e8Splunky {
324bd42c2e8Splunky 	uint8_t			buf[HCI_EVENT_PKT_SIZE], *p;
325bd42c2e8Splunky 	hci_event_hdr_t		ev;
326bd42c2e8Splunky 	hci_command_status_ep	cs;
327bd42c2e8Splunky 	hci_command_compl_ep	cc;
328bd42c2e8Splunky 	time_t			to;
329bd42c2e8Splunky 	ssize_t			n;
330bd42c2e8Splunky 
331bd42c2e8Splunky 	n = bt_devsend(s, req->opcode, req->cparam, req->clen);
332bd42c2e8Splunky 	if (n == -1)
333bd42c2e8Splunky 		return errno;
334bd42c2e8Splunky 
335bd42c2e8Splunky 	for (;;) {
336bd42c2e8Splunky 		to = t_end - time(NULL);
337bd42c2e8Splunky 		if (to < 0)
338bd42c2e8Splunky 			return ETIMEDOUT;
339bd42c2e8Splunky 
340bd42c2e8Splunky 		p = buf;
341bd42c2e8Splunky 		n = bt_devrecv(s, buf, sizeof(buf), to);
342bd42c2e8Splunky 		if (n == -1)
343bd42c2e8Splunky 			return errno;
344bd42c2e8Splunky 
345bd42c2e8Splunky 		if (sizeof(ev) > (size_t)n || p[0] != HCI_EVENT_PKT)
346bd42c2e8Splunky 			return EIO;
347bd42c2e8Splunky 
348bd42c2e8Splunky 		memcpy(&ev, p, sizeof(ev));
349bd42c2e8Splunky 		p += sizeof(ev);
350bd42c2e8Splunky 		n -= sizeof(ev);
351bd42c2e8Splunky 
352bd42c2e8Splunky 		if (ev.event == req->event)
353bd42c2e8Splunky 			break;
354bd42c2e8Splunky 
355bd42c2e8Splunky 		if (ev.event == HCI_EVENT_COMMAND_STATUS) {
356bd42c2e8Splunky 			if (sizeof(cs) > (size_t)n)
357bd42c2e8Splunky 				return EIO;
358bd42c2e8Splunky 
359bd42c2e8Splunky 			memcpy(&cs, p, sizeof(cs));
360bd42c2e8Splunky 			p += sizeof(cs);
361bd42c2e8Splunky 			n -= sizeof(cs);
362bd42c2e8Splunky 
363bd42c2e8Splunky 			if (le16toh(cs.opcode) == req->opcode) {
364bd42c2e8Splunky 				if (cs.status != 0)
365bd42c2e8Splunky 					return EIO;
366bd42c2e8Splunky 
367bd42c2e8Splunky 				if (req->event == 0)
368bd42c2e8Splunky 					break;
369bd42c2e8Splunky 			}
370bd42c2e8Splunky 
371bd42c2e8Splunky 			continue;
372bd42c2e8Splunky 		}
373bd42c2e8Splunky 
374bd42c2e8Splunky 		if (ev.event == HCI_EVENT_COMMAND_COMPL) {
375bd42c2e8Splunky 			if (sizeof(cc) > (size_t)n)
376bd42c2e8Splunky 				return EIO;
377bd42c2e8Splunky 
378bd42c2e8Splunky 			memcpy(&cc, p, sizeof(cc));
379bd42c2e8Splunky 			p += sizeof(cc);
380bd42c2e8Splunky 			n -= sizeof(cc);
381bd42c2e8Splunky 
382bd42c2e8Splunky 			if (le16toh(cc.opcode) == req->opcode)
383bd42c2e8Splunky 				break;
384bd42c2e8Splunky 
385bd42c2e8Splunky 			continue;
386bd42c2e8Splunky 		}
387bd42c2e8Splunky 	}
388bd42c2e8Splunky 
389bd42c2e8Splunky 	/* copy out response data */
390bd42c2e8Splunky 	if (req->rlen >= (size_t)n) {
391bd42c2e8Splunky 		req->rlen = n;
392bd42c2e8Splunky 		memcpy(req->rparam, p, req->rlen);
393bd42c2e8Splunky 	} else if (req->rlen > 0)
394bd42c2e8Splunky 		return EIO;
395bd42c2e8Splunky 
396bd42c2e8Splunky 	return 0;
397bd42c2e8Splunky }
398bd42c2e8Splunky 
399bd42c2e8Splunky int
bt_devreq(int s,struct bt_devreq * req,time_t to)400bd42c2e8Splunky bt_devreq(int s, struct bt_devreq *req, time_t to)
401bd42c2e8Splunky {
402bd42c2e8Splunky 	struct bt_devfilter	new, old;
403bd42c2e8Splunky 	int			error;
404bd42c2e8Splunky 
405bd42c2e8Splunky 	if (req == NULL || to < 0
406bd42c2e8Splunky 	    || (req->rlen == 0 && req->rparam != NULL)
407bd42c2e8Splunky 	    || (req->rlen != 0 && req->rparam == NULL)) {
408bd42c2e8Splunky 		errno = EINVAL;
409bd42c2e8Splunky 		return -1;
410bd42c2e8Splunky 	}
411bd42c2e8Splunky 
412bd42c2e8Splunky 	memset(&new, 0, sizeof(new));
413bd42c2e8Splunky 	bt_devfilter_pkt_set(&new, HCI_EVENT_PKT);
414bd42c2e8Splunky 	bt_devfilter_evt_set(&new, HCI_EVENT_COMMAND_COMPL);
415bd42c2e8Splunky 	bt_devfilter_evt_set(&new, HCI_EVENT_COMMAND_STATUS);
416bd42c2e8Splunky 
417bd42c2e8Splunky 	if (req->event != 0)
418bd42c2e8Splunky 		bt_devfilter_evt_set(&new, req->event);
419bd42c2e8Splunky 
420bd42c2e8Splunky 	if (bt_devfilter(s, &new, &old) == -1)
421bd42c2e8Splunky 		return -1;
422bd42c2e8Splunky 
423bd42c2e8Splunky 	error = bt__devreq(s, req, to + time(NULL));
424bd42c2e8Splunky 
425bd42c2e8Splunky 	(void)bt_devfilter(s, &old, NULL);
426bd42c2e8Splunky 
427bd42c2e8Splunky 	if (error != 0) {
428bd42c2e8Splunky 		errno = error;
429bd42c2e8Splunky 		return -1;
430bd42c2e8Splunky 	}
431bd42c2e8Splunky 
432bd42c2e8Splunky 	return 0;
433bd42c2e8Splunky }
434bd42c2e8Splunky 
435bd42c2e8Splunky int
bt_devfilter(int s,const struct bt_devfilter * new,struct bt_devfilter * old)436bd42c2e8Splunky bt_devfilter(int s, const struct bt_devfilter *new, struct bt_devfilter *old)
437bd42c2e8Splunky {
438bd42c2e8Splunky 	socklen_t	len;
439bd42c2e8Splunky 
440bd42c2e8Splunky 	if (new == NULL && old == NULL) {
441bd42c2e8Splunky 		errno = EINVAL;
442bd42c2e8Splunky 		return -1;
443bd42c2e8Splunky 	}
444bd42c2e8Splunky 
445bd42c2e8Splunky 	len = sizeof(struct hci_filter);
446bd42c2e8Splunky 
447bd42c2e8Splunky 	if (old != NULL) {
448bd42c2e8Splunky 		if (getsockopt(s, BTPROTO_HCI,
449bd42c2e8Splunky 		    SO_HCI_PKT_FILTER, &old->packet_mask, &len) == -1
450bd42c2e8Splunky 		    || len != sizeof(struct hci_filter))
451bd42c2e8Splunky 			return -1;
452bd42c2e8Splunky 
453bd42c2e8Splunky 		if (getsockopt(s, BTPROTO_HCI,
454bd42c2e8Splunky 		    SO_HCI_EVT_FILTER, &old->event_mask, &len) == -1
455bd42c2e8Splunky 		    || len != sizeof(struct hci_filter))
456bd42c2e8Splunky 			return -1;
457bd42c2e8Splunky 	}
458bd42c2e8Splunky 
459bd42c2e8Splunky 	if (new != NULL) {
460bd42c2e8Splunky 		if (setsockopt(s, BTPROTO_HCI,
461bd42c2e8Splunky 		    SO_HCI_PKT_FILTER, &new->packet_mask, len) == -1)
462bd42c2e8Splunky 			return -1;
463bd42c2e8Splunky 
464bd42c2e8Splunky 		if (setsockopt(s, BTPROTO_HCI,
465bd42c2e8Splunky 		    SO_HCI_EVT_FILTER, &new->event_mask, len) == -1)
466bd42c2e8Splunky 			return -1;
467bd42c2e8Splunky 	}
468bd42c2e8Splunky 
469bd42c2e8Splunky 	return 0;
470bd42c2e8Splunky }
471bd42c2e8Splunky 
472bd42c2e8Splunky void
bt_devfilter_pkt_set(struct bt_devfilter * filter,uint8_t type)473bd42c2e8Splunky bt_devfilter_pkt_set(struct bt_devfilter *filter, uint8_t type)
474bd42c2e8Splunky {
475bd42c2e8Splunky 
476bd42c2e8Splunky 	hci_filter_set(type, &filter->packet_mask);
477bd42c2e8Splunky }
478bd42c2e8Splunky 
479bd42c2e8Splunky void
bt_devfilter_pkt_clr(struct bt_devfilter * filter,uint8_t type)480bd42c2e8Splunky bt_devfilter_pkt_clr(struct bt_devfilter *filter, uint8_t type)
481bd42c2e8Splunky {
482bd42c2e8Splunky 
483bd42c2e8Splunky 	hci_filter_clr(type, &filter->packet_mask);
484bd42c2e8Splunky }
485bd42c2e8Splunky 
486bd42c2e8Splunky int
bt_devfilter_pkt_tst(const struct bt_devfilter * filter,uint8_t type)487bd42c2e8Splunky bt_devfilter_pkt_tst(const struct bt_devfilter *filter, uint8_t type)
488bd42c2e8Splunky {
489bd42c2e8Splunky 
490bd42c2e8Splunky 	return hci_filter_test(type, &filter->packet_mask);
491bd42c2e8Splunky }
492bd42c2e8Splunky 
493bd42c2e8Splunky void
bt_devfilter_evt_set(struct bt_devfilter * filter,uint8_t event)494bd42c2e8Splunky bt_devfilter_evt_set(struct bt_devfilter *filter, uint8_t event)
495bd42c2e8Splunky {
496bd42c2e8Splunky 
497bd42c2e8Splunky 	hci_filter_set(event, &filter->event_mask);
498bd42c2e8Splunky }
499bd42c2e8Splunky 
500bd42c2e8Splunky void
bt_devfilter_evt_clr(struct bt_devfilter * filter,uint8_t event)501bd42c2e8Splunky bt_devfilter_evt_clr(struct bt_devfilter *filter, uint8_t event)
502bd42c2e8Splunky {
503bd42c2e8Splunky 
504bd42c2e8Splunky 	hci_filter_clr(event, &filter->event_mask);
505bd42c2e8Splunky }
506bd42c2e8Splunky 
507bd42c2e8Splunky int
bt_devfilter_evt_tst(const struct bt_devfilter * filter,uint8_t event)508bd42c2e8Splunky bt_devfilter_evt_tst(const struct bt_devfilter *filter, uint8_t event)
509bd42c2e8Splunky {
510bd42c2e8Splunky 
511bd42c2e8Splunky 	return hci_filter_test(event, &filter->event_mask);
512bd42c2e8Splunky }
513bd42c2e8Splunky 
514bd42c2e8Splunky /*
515bd42c2e8Splunky  * Internal function used by bt_devinquiry to find the first
516bd42c2e8Splunky  * active device.
517bd42c2e8Splunky  */
5189dc6fb5cSplunky /* ARGSUSED */
519bd42c2e8Splunky static int
bt__devany_cb(int s,const struct bt_devinfo * info,void * arg)520bd42c2e8Splunky bt__devany_cb(int s, const struct bt_devinfo *info, void *arg)
521bd42c2e8Splunky {
522bd42c2e8Splunky 
523bd42c2e8Splunky 	if ((info->enabled)) {
524bd42c2e8Splunky 		strlcpy(arg, info->devname, HCI_DEVNAME_SIZE + 1);
525bd42c2e8Splunky 		return 1;
526bd42c2e8Splunky 	}
527bd42c2e8Splunky 
528bd42c2e8Splunky 	return 0;
529bd42c2e8Splunky }
530bd42c2e8Splunky 
531bd42c2e8Splunky /*
532bd42c2e8Splunky  * Internal function used by bt_devinquiry to insert inquiry
533bd42c2e8Splunky  * results to an array. Make sure that a bdaddr only appears
534bd42c2e8Splunky  * once in the list and always use the latest result.
535bd42c2e8Splunky  */
536bd42c2e8Splunky static void
bt__devresult(struct bt_devinquiry * ii,int * count,int max_count,bdaddr_t * ba,uint8_t psrm,uint8_t pspm,uint8_t * cl,uint16_t co,int8_t rssi,uint8_t * data)537bd42c2e8Splunky bt__devresult(struct bt_devinquiry *ii, int *count, int max_count,
538bd42c2e8Splunky     bdaddr_t *ba, uint8_t psrm, uint8_t pspm, uint8_t *cl, uint16_t co,
539bd42c2e8Splunky     int8_t rssi, uint8_t *data)
540bd42c2e8Splunky {
541bd42c2e8Splunky 	int	n;
542bd42c2e8Splunky 
543bd42c2e8Splunky 	for (n = 0; ; n++, ii++) {
544bd42c2e8Splunky 		if (n == *count) {
545bd42c2e8Splunky 			if (*count == max_count)
546bd42c2e8Splunky 				return;
547bd42c2e8Splunky 
548bd42c2e8Splunky 			(*count)++;
549bd42c2e8Splunky 			break;
550bd42c2e8Splunky 		}
551bd42c2e8Splunky 
552bd42c2e8Splunky 		if (bdaddr_same(&ii->bdaddr, ba))
553bd42c2e8Splunky 			break;
554bd42c2e8Splunky 	}
555bd42c2e8Splunky 
556bd42c2e8Splunky 	bdaddr_copy(&ii->bdaddr, ba);
557bd42c2e8Splunky 	ii->pscan_rep_mode = psrm;
558bd42c2e8Splunky 	ii->pscan_period_mode = pspm;
559bd42c2e8Splunky 	ii->clock_offset = le16toh(co);
560bd42c2e8Splunky 	ii->rssi = rssi;
561bd42c2e8Splunky 
562bd42c2e8Splunky 	if (cl != NULL)
563bd42c2e8Splunky 		memcpy(ii->dev_class, cl, HCI_CLASS_SIZE);
564bd42c2e8Splunky 
565bd42c2e8Splunky 	if (data != NULL)
566bd42c2e8Splunky 		memcpy(ii->data, data, 240);
567bd42c2e8Splunky }
568bd42c2e8Splunky 
569bd42c2e8Splunky int
bt_devinquiry(const char * name,time_t to,int max_rsp,struct bt_devinquiry ** iip)570bd42c2e8Splunky bt_devinquiry(const char *name, time_t to, int max_rsp,
571bd42c2e8Splunky     struct bt_devinquiry **iip)
572bd42c2e8Splunky {
573bd42c2e8Splunky 	uint8_t			buf[HCI_EVENT_PKT_SIZE], *p;
574bd42c2e8Splunky 	struct bt_devfilter	f;
575bd42c2e8Splunky 	hci_event_hdr_t		ev;
576bd42c2e8Splunky 	hci_command_status_ep	sp;
577bd42c2e8Splunky 	hci_inquiry_cp		cp;
578bd42c2e8Splunky 	hci_inquiry_result_ep	ip;
579bd42c2e8Splunky 	hci_inquiry_response	ir;
580bd42c2e8Splunky 	hci_rssi_result_ep	rp;
581bd42c2e8Splunky 	hci_rssi_response	rr;
582bd42c2e8Splunky 	hci_extended_result_ep	ep;
583bd42c2e8Splunky 	struct bt_devinquiry	*ii;
584bd42c2e8Splunky 	int			count, i, s;
585bd42c2e8Splunky 	time_t			t_end;
586bd42c2e8Splunky 	ssize_t			n;
587bd42c2e8Splunky 
588bd42c2e8Splunky 	if (iip == NULL) {
589bd42c2e8Splunky 		errno = EINVAL;
590bd42c2e8Splunky 		return -1;
591bd42c2e8Splunky 	}
592bd42c2e8Splunky 
593bd42c2e8Splunky 	if (name == NULL) {
594bd42c2e8Splunky 		if (bt_devenum(bt__devany_cb, buf) == -1)
595bd42c2e8Splunky 			return -1;
596bd42c2e8Splunky 
597bd42c2e8Splunky 		name = (const char *)buf;
598bd42c2e8Splunky 	}
599bd42c2e8Splunky 
600bd42c2e8Splunky 	s = bt_devopen(name, 0);
601bd42c2e8Splunky 	if (s == -1)
602bd42c2e8Splunky 		return -1;
603bd42c2e8Splunky 
604bd42c2e8Splunky 	memset(&f, 0, sizeof(f));
605bd42c2e8Splunky 	bt_devfilter_pkt_set(&f, HCI_EVENT_PKT);
606bd42c2e8Splunky 	bt_devfilter_evt_set(&f, HCI_EVENT_COMMAND_STATUS);
607bd42c2e8Splunky 	bt_devfilter_evt_set(&f, HCI_EVENT_INQUIRY_COMPL);
608bd42c2e8Splunky 	bt_devfilter_evt_set(&f, HCI_EVENT_INQUIRY_RESULT);
609bd42c2e8Splunky 	bt_devfilter_evt_set(&f, HCI_EVENT_RSSI_RESULT);
610bd42c2e8Splunky 	bt_devfilter_evt_set(&f, HCI_EVENT_EXTENDED_RESULT);
611bd42c2e8Splunky 	if (bt_devfilter(s, &f, NULL) == -1) {
612bd42c2e8Splunky 		close(s);
613bd42c2e8Splunky 		return -1;
614bd42c2e8Splunky 	}
615bd42c2e8Splunky 
616bd42c2e8Splunky 	/*
617*37a23ecfSmsaitoh 	 * silently adjust number of responses to fit in uint8_t
618bd42c2e8Splunky 	 */
619bd42c2e8Splunky 	if (max_rsp < 1)
620bd42c2e8Splunky 		max_rsp = 8;
621bd42c2e8Splunky 	else if (max_rsp > UINT8_MAX)
622bd42c2e8Splunky 		max_rsp = UINT8_MAX;
623bd42c2e8Splunky 
624bd42c2e8Splunky 	ii = calloc((size_t)max_rsp, sizeof(struct bt_devinquiry));
625bd42c2e8Splunky 	if (ii == NULL) {
626bd42c2e8Splunky 		close(s);
627bd42c2e8Splunky 		return -1;
628bd42c2e8Splunky 	}
629bd42c2e8Splunky 
630bd42c2e8Splunky 	/*
631bd42c2e8Splunky 	 * silently adjust timeout value so that inquiry_length
632bd42c2e8Splunky 	 * falls into the range 0x01->0x30 (unit is 1.28 seconds)
633bd42c2e8Splunky 	 */
634bd42c2e8Splunky 	if (to < 1)
635bd42c2e8Splunky 		to = 5;
636bd42c2e8Splunky 	else if (to == 1)
637bd42c2e8Splunky 		to = 2;
638bd42c2e8Splunky 	else if (to > 62)
639bd42c2e8Splunky 		to = 62;
640bd42c2e8Splunky 
641bd42c2e8Splunky 	/* General Inquiry LAP is 0x9e8b33 */
642bd42c2e8Splunky 	cp.lap[0] = 0x33;
643bd42c2e8Splunky 	cp.lap[1] = 0x8b;
644bd42c2e8Splunky 	cp.lap[2] = 0x9e;
645bd42c2e8Splunky 	cp.inquiry_length = (uint8_t)(to * 100 / 128);
646bd42c2e8Splunky 	cp.num_responses = (uint8_t)max_rsp;
647bd42c2e8Splunky 
648bd42c2e8Splunky 	if (bt_devsend(s, HCI_CMD_INQUIRY, &cp, sizeof(cp)) == -1)
649bd42c2e8Splunky 		goto fail;
650bd42c2e8Splunky 
651bd42c2e8Splunky 	count = 0;
652bd42c2e8Splunky 
653bd42c2e8Splunky 	for (t_end = time(NULL) + to + 1; to > 0; to = t_end - time(NULL)) {
654bd42c2e8Splunky 		p = buf;
655bd42c2e8Splunky 		n = bt_devrecv(s, buf, sizeof(buf), to);
656bd42c2e8Splunky 		if (n == -1)
657bd42c2e8Splunky 			goto fail;
658bd42c2e8Splunky 
659bd42c2e8Splunky 		if (sizeof(ev) > (size_t)n) {
660bd42c2e8Splunky 			errno = EIO;
661bd42c2e8Splunky 			goto fail;
662bd42c2e8Splunky 		}
663bd42c2e8Splunky 
664bd42c2e8Splunky 		memcpy(&ev, p, sizeof(ev));
665bd42c2e8Splunky 		p += sizeof(ev);
666bd42c2e8Splunky 		n -= sizeof(ev);
667bd42c2e8Splunky 
668bd42c2e8Splunky 		switch (ev.event) {
669bd42c2e8Splunky 		case HCI_EVENT_COMMAND_STATUS:
670bd42c2e8Splunky 			if (sizeof(sp) > (size_t)n)
671bd42c2e8Splunky 				break;
672bd42c2e8Splunky 
673bd42c2e8Splunky 			memcpy(&sp, p, sizeof(sp));
674bd42c2e8Splunky 
675bd42c2e8Splunky 			if (le16toh(sp.opcode) != HCI_CMD_INQUIRY
676bd42c2e8Splunky 			    || sp.status == 0)
677bd42c2e8Splunky 				break;
678bd42c2e8Splunky 
679bd42c2e8Splunky 			errno = EIO;
680bd42c2e8Splunky 			goto fail;
681bd42c2e8Splunky 
682bd42c2e8Splunky 		case HCI_EVENT_INQUIRY_COMPL:
683bd42c2e8Splunky 			close(s);
684bd42c2e8Splunky 			*iip = ii;
685bd42c2e8Splunky 			return count;
686bd42c2e8Splunky 
687bd42c2e8Splunky 		case HCI_EVENT_INQUIRY_RESULT:
688bd42c2e8Splunky 			if (sizeof(ip) > (size_t)n)
689bd42c2e8Splunky 				break;
690bd42c2e8Splunky 
691bd42c2e8Splunky 			memcpy(&ip, p, sizeof(ip));
692bd42c2e8Splunky 			p += sizeof(ip);
693bd42c2e8Splunky 			n -= sizeof(ip);
694bd42c2e8Splunky 
695bd42c2e8Splunky 			if (sizeof(ir) * ip.num_responses != (size_t)n)
696bd42c2e8Splunky 				break;
697bd42c2e8Splunky 
698bd42c2e8Splunky 			for (i = 0; i < ip.num_responses; i++) {
699bd42c2e8Splunky 				memcpy(&ir, p, sizeof(ir));
700bd42c2e8Splunky 				p += sizeof(ir);
701bd42c2e8Splunky 
702bd42c2e8Splunky 				bt__devresult(ii, &count, max_rsp,
703bd42c2e8Splunky 					&ir.bdaddr,
704bd42c2e8Splunky 					ir.page_scan_rep_mode,
705bd42c2e8Splunky 					ir.page_scan_period_mode,
706bd42c2e8Splunky 					ir.uclass,
707bd42c2e8Splunky 					ir.clock_offset,
708bd42c2e8Splunky 					0,		/* rssi */
709bd42c2e8Splunky 					NULL);		/* extended data */
710bd42c2e8Splunky 			}
711bd42c2e8Splunky 
712bd42c2e8Splunky 			break;
713bd42c2e8Splunky 
714bd42c2e8Splunky 		case HCI_EVENT_RSSI_RESULT:
715bd42c2e8Splunky 			if (sizeof(rp) > (size_t)n)
716bd42c2e8Splunky 				break;
717bd42c2e8Splunky 
718bd42c2e8Splunky 			memcpy(&rp, p, sizeof(rp));
719bd42c2e8Splunky 			p += sizeof(rp);
720bd42c2e8Splunky 			n -= sizeof(rp);
721bd42c2e8Splunky 
722bd42c2e8Splunky 			if (sizeof(rr) * rp.num_responses != (size_t)n)
723bd42c2e8Splunky 				break;
724bd42c2e8Splunky 
725bd42c2e8Splunky 			for (i = 0; i < rp.num_responses; i++) {
726bd42c2e8Splunky 				memcpy(&rr, p, sizeof(rr));
727bd42c2e8Splunky 				p += sizeof(rr);
728bd42c2e8Splunky 
729bd42c2e8Splunky 				bt__devresult(ii, &count, max_rsp,
730bd42c2e8Splunky 					&rr.bdaddr,
731bd42c2e8Splunky 					rr.page_scan_rep_mode,
732bd42c2e8Splunky 					0,	/* page scan period mode */
733bd42c2e8Splunky 					rr.uclass,
734bd42c2e8Splunky 					rr.clock_offset,
735bd42c2e8Splunky 					rr.rssi,
736bd42c2e8Splunky 					NULL);		/* extended data */
737bd42c2e8Splunky 			}
738bd42c2e8Splunky 
739bd42c2e8Splunky 			break;
740bd42c2e8Splunky 
741bd42c2e8Splunky 		case HCI_EVENT_EXTENDED_RESULT:
742bd42c2e8Splunky 			if (sizeof(ep) != (size_t)n)
743bd42c2e8Splunky 				break;
744bd42c2e8Splunky 
745bd42c2e8Splunky 			memcpy(&ep, p, sizeof(ep));
746bd42c2e8Splunky 
747bd42c2e8Splunky 			if (ep.num_responses != 1)
748bd42c2e8Splunky 				break;
749bd42c2e8Splunky 
750bd42c2e8Splunky 			bt__devresult(ii, &count, max_rsp,
751bd42c2e8Splunky 				&ep.bdaddr,
752bd42c2e8Splunky 				ep.page_scan_rep_mode,
753bd42c2e8Splunky 				0,	/* page scan period mode */
754bd42c2e8Splunky 				ep.uclass,
755bd42c2e8Splunky 				ep.clock_offset,
756bd42c2e8Splunky 				ep.rssi,
757bd42c2e8Splunky 				ep.response);
758bd42c2e8Splunky 
759bd42c2e8Splunky 			break;
760bd42c2e8Splunky 
761bd42c2e8Splunky 		default:
762bd42c2e8Splunky 			break;
763bd42c2e8Splunky 		}
764bd42c2e8Splunky 	}
765bd42c2e8Splunky 
766bd42c2e8Splunky 	errno = ETIMEDOUT;
767bd42c2e8Splunky 
768bd42c2e8Splunky fail:
769bd42c2e8Splunky 	free(ii);
770bd42c2e8Splunky 	close(s);
771bd42c2e8Splunky 	return -1;
772bd42c2e8Splunky }
773bd42c2e8Splunky 
774bd42c2e8Splunky /*
775bd42c2e8Splunky  * Internal version of bt_devinfo. Fill in the devinfo structure
77633166e16Splunky  * with the socket handle provided.
777bd42c2e8Splunky  */
778bd42c2e8Splunky static int
bt__devinfo(int s,const char * name,struct bt_devinfo * info)779bd42c2e8Splunky bt__devinfo(int s, const char *name, struct bt_devinfo *info)
780bd42c2e8Splunky {
781bd42c2e8Splunky 	struct btreq			btr;
782bd42c2e8Splunky 
783bd42c2e8Splunky 	memset(&btr, 0, sizeof(btr));
784ae8d4ecfSchristos 	strlcpy(btr.btr_name, name, HCI_DEVNAME_SIZE);
785bd42c2e8Splunky 
786bd42c2e8Splunky 	if (ioctl(s, SIOCGBTINFO, &btr) == -1)
787bd42c2e8Splunky 		return -1;
788bd42c2e8Splunky 
789bd42c2e8Splunky 	memset(info, 0, sizeof(struct bt_devinfo));
790bd42c2e8Splunky 	memcpy(info->devname, btr.btr_name, HCI_DEVNAME_SIZE);
791bd42c2e8Splunky 	bdaddr_copy(&info->bdaddr, &btr.btr_bdaddr);
792bd42c2e8Splunky 	info->enabled = ((btr.btr_flags & BTF_UP) ? 1 : 0);
793bd42c2e8Splunky 
794bd42c2e8Splunky 	info->sco_size = btr.btr_sco_mtu;
795bd42c2e8Splunky 	info->acl_size = btr.btr_acl_mtu;
796bd42c2e8Splunky 	info->cmd_free = btr.btr_num_cmd;
797bd42c2e8Splunky 	info->sco_free = btr.btr_num_sco;
798bd42c2e8Splunky 	info->acl_free = btr.btr_num_acl;
79933166e16Splunky 	info->sco_pkts = btr.btr_max_sco;
80033166e16Splunky 	info->acl_pkts = btr.btr_max_acl;
801bd42c2e8Splunky 
802bd42c2e8Splunky 	info->link_policy_info = btr.btr_link_policy;
803bd42c2e8Splunky 	info->packet_type_info = btr.btr_packet_type;
804bd42c2e8Splunky 
80533166e16Splunky 	if (ioctl(s, SIOCGBTFEAT, &btr) == -1)
80633166e16Splunky 		return -1;
80733166e16Splunky 
80833166e16Splunky 	memcpy(info->features, btr.btr_features0, HCI_FEATURES_SIZE);
80933166e16Splunky 
810bd42c2e8Splunky 	if (ioctl(s, SIOCGBTSTATS, &btr) == -1)
811bd42c2e8Splunky 		return -1;
812bd42c2e8Splunky 
813bd42c2e8Splunky 	info->cmd_sent = btr.btr_stats.cmd_tx;
814bd42c2e8Splunky 	info->evnt_recv = btr.btr_stats.evt_rx;
815bd42c2e8Splunky 	info->acl_recv = btr.btr_stats.acl_rx;
816bd42c2e8Splunky 	info->acl_sent = btr.btr_stats.acl_tx;
817bd42c2e8Splunky 	info->sco_recv = btr.btr_stats.sco_rx;
818bd42c2e8Splunky 	info->sco_sent = btr.btr_stats.sco_tx;
819bd42c2e8Splunky 	info->bytes_recv = btr.btr_stats.byte_rx;
820bd42c2e8Splunky 	info->bytes_sent = btr.btr_stats.byte_tx;
821bd42c2e8Splunky 
822bd42c2e8Splunky 	return 0;
823bd42c2e8Splunky }
824bd42c2e8Splunky 
825bd42c2e8Splunky int
bt_devinfo(const char * name,struct bt_devinfo * info)826bd42c2e8Splunky bt_devinfo(const char *name, struct bt_devinfo *info)
827bd42c2e8Splunky {
828bd42c2e8Splunky 	int	rv, s;
829bd42c2e8Splunky 
830bd42c2e8Splunky 	if (name == NULL || info == NULL) {
831bd42c2e8Splunky 		errno = EINVAL;
832bd42c2e8Splunky 		return -1;
833bd42c2e8Splunky 	}
834bd42c2e8Splunky 
835bd42c2e8Splunky 	s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
836bd42c2e8Splunky 	if (s == -1)
837bd42c2e8Splunky 		return -1;
838bd42c2e8Splunky 
839bd42c2e8Splunky 	rv = bt__devinfo(s, name, info);
840bd42c2e8Splunky 	close(s);
841bd42c2e8Splunky 	return rv;
842bd42c2e8Splunky }
843bd42c2e8Splunky 
844bd42c2e8Splunky int
bt_devenum(bt_devenum_cb_t cb,void * arg)845bd42c2e8Splunky bt_devenum(bt_devenum_cb_t cb, void *arg)
846bd42c2e8Splunky {
847bd42c2e8Splunky 	struct btreq		btr;
848bd42c2e8Splunky 	struct bt_devinfo	info;
84933166e16Splunky 	struct sockaddr_bt	sa;
850bd42c2e8Splunky 	int			count, fd, rv, s;
851bd42c2e8Splunky 
852bd42c2e8Splunky 	s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
853bd42c2e8Splunky 	if (s == -1)
854bd42c2e8Splunky 		return -1;
855bd42c2e8Splunky 
856bd42c2e8Splunky 	memset(&btr, 0, sizeof(btr));
857bd42c2e8Splunky 	count = 0;
858bd42c2e8Splunky 
859bd42c2e8Splunky 	while (ioctl(s, SIOCNBTINFO, &btr) != -1) {
860bd42c2e8Splunky 		count++;
861bd42c2e8Splunky 
862bd42c2e8Splunky 		if (cb == NULL)
863bd42c2e8Splunky 			continue;
864bd42c2e8Splunky 
865bd42c2e8Splunky 		fd = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
866bd42c2e8Splunky 		if (fd == -1) {
867bd42c2e8Splunky 			close(s);
868bd42c2e8Splunky 			return -1;
869bd42c2e8Splunky 		}
870bd42c2e8Splunky 
871bd42c2e8Splunky 		if (bt__devinfo(fd, btr.btr_name, &info) == -1) {
872bd42c2e8Splunky 			close(fd);
873bd42c2e8Splunky 			close(s);
874bd42c2e8Splunky 			return -1;
875bd42c2e8Splunky 		}
876bd42c2e8Splunky 
87733166e16Splunky 		if (info.enabled) {
87833166e16Splunky 			memset(&sa, 0, sizeof(sa));
87933166e16Splunky 			sa.bt_len = sizeof(sa);
88033166e16Splunky 			sa.bt_family = AF_BLUETOOTH;
88133166e16Splunky 			bdaddr_copy(&sa.bt_bdaddr, &info.bdaddr);
88233166e16Splunky 
88333166e16Splunky 			if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1
88433166e16Splunky 			    || connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
88533166e16Splunky 				close(fd);
88633166e16Splunky 				close(s);
88733166e16Splunky 				return -1;
88833166e16Splunky 			}
88933166e16Splunky 		}
89033166e16Splunky 
891bd42c2e8Splunky 		rv = (*cb)(fd, &info, arg);
892bd42c2e8Splunky 		close(fd);
893bd42c2e8Splunky 		if (rv != 0)
894bd42c2e8Splunky 			break;
895bd42c2e8Splunky 	}
896bd42c2e8Splunky 
897bd42c2e8Splunky 	close(s);
898bd42c2e8Splunky 	return count;
899bd42c2e8Splunky }
900