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