libhci update

Maksim Yevmenkin emax at freebsd.org
Mon Apr 6 13:58:45 PDT 2009


Hi Bruce,

> So I decided to just go ahead and reimplement Markus Brueffer's libhci.
> I've attached a tarball of the Hg repository.

thanks!

> It would be great to have feedback from you about this.

right, a few things.

- dev_id is kinda gross, imo. Iain and i discussed this and agreed
that devname is the way to go. mapping between dev_id and devname can
be (has to be) done and i have no objection to this, however, all the
native api probably should use devname and not dev_id;

- all the hci_xxx functions (with possible exception of inquiry)
should probably be left out of the library. there is really not enough
consumers for them;

- i not sure i like, hci_xxx names; Iain and i have been discussing
this before and agreed that, bt_XXX and bt_devXXXX are better names;

i'm attaching the diffs that was sitting in my queue (still need to
update man pages). please take a look at it and let me know what you
think.

> The API is now compatible with the current BlueZ SVN code. One of the
> trade-offs involved was choosing an identifier which would fit in an 'int'
> type, as BlueZ identifies its devices using uint16_t.
>
> As a compromise, I wrote code which emulates BlueZ device enumeration
> using the Netgraph node ID (32 bits wide). Unfortunately this introduces
> a dependency on -lnetgraph, unless the Bluetooth stack is itself taught to
> produce such an identifier (and supply it via an ioctl()).

i actually went this route before :) but i opted for much simpler scheme:

#define hci_devid2type(id)      (((id) >> 12) & 0x0f)
#define hci_devid2unit(id)      ((id) & 0x0fff)
#define hci_mkdevid(type, unit) ((((type) & 0x0f) << 12) | (unit & 0x0fff))

struct hci_type2prefix {
        int             type;
        char const      *prefix;
};

static struct hci_type2prefix const     type2prefix[] =
{
        { .type = HCI_VIRTUAL,  .prefix = NULL,    },
        { .type = HCI_USB,      .prefix = "ubt",   },
        { .type = HCI_PCCARD,   .prefix = "btccc", },
        { .type = HCI_UART,     .prefix = "h4",    },
        { .type = HCI_RS232,    .prefix = NULL,    },
        { .type = HCI_PCI,      .prefix = NULL,    },
        { .type = HCI_SDIO,     .prefix = NULL,    },
        { .type = -1,           .prefix = NULL,    },   /* should be last */
};

static int
hci_name2devid(char const *name)
{
        struct hci_type2prefix const    *t;
        int                             plen, unit;
        char                            *ep;

        for (t = &type2prefix[0]; t->type != -1; t ++) {
                if (t->prefix == NULL)
                        continue;

                plen = strlen(t->prefix);
                if (strncmp(name, t->prefix, plen) != 0)
                        continue;

                unit = strtoul(name + plen, &ep, 10);
                if (*ep != '\0' &&
                    strcmp(ep, "hci") != 0 &&
                    strcmp(ep, "l2cap") != 0) {
                        errno = ENODEV;
                        return (-1);
                }

                return (hci_mkdevid(t->type, unit));
        }

        errno = ENODEV;

        return (-1);
}

static char *
hci_devid2name(int dev_id, char *name, int namelen)
{
        struct hci_type2prefix const    *t;
        int                             type, unit;

        if (dev_id >= 0) {
                type = hci_devid2type(dev_id);
                unit = hci_devid2unit(dev_id);

                for (t = &type2prefix[0]; t->type != -1; t ++) {
                        if (t->type == type && t->prefix != NULL) {
                                snprintf(name, namelen, "%s%uhci",
                                        t->prefix, unit);
                                return (name);
                        }
                }
        }

        errno = EINVAL;

        return (NULL);
}

this code is left out for now. not sure if i like it :)

> I noticed during the port that periodic inquiry doesn't actually buy us
> anything
> at all. With a CSR BlueCore4-ROM dongle, the microcontroller will not
> raise any other events *at all* at the HCI layer during inquiry.
>
> Mind you, I haven't tried any of the Bluetooth HCI 2.0/EDR commands,
> many of these are not yet implemented in the FreeBSD stack, am I right?

well, most of 2.0 commands do not need any implementation. just need
to add defines and typedefs. some events might need handling, i.e.
inquiry with rssi. but generally it should just work.

> I also had a crack at porting NetBSD's 'btconfig'. I haven't included it in
> this drop, however, it does depend on the libhci functions I have written.

how about just fixing hcicontrol(8) do to that you want? is this too much work?

> I see that NetBSD's libsdp does support 128 bit UUIDs. Whilst this is
> highly desirable, it is also a prerequisite for any of the high level
> language
> wrappers (e.g. PyBlueZ, BlueCove) which make use of SDP.

ok, i will take a look at the diffs to see what is different.

> Anyway, let me know what you think. I am eager to get the SDP issues
> knocked on the head ASAP.

right, that would likely be complete replacement of libsdp and sdpd.

thanks,
max
-------------- next part --------------
Index: hci.c
===================================================================
--- hci.c	(revision 190594)
+++ hci.c	(working copy)
@@ -30,15 +30,421 @@
  * $FreeBSD$
  */
 
+#include <assert.h>
 #include <bluetooth.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 
+static int    bt_devany_cb(int s, struct bt_devinfo const *di, void *xdevname);
 static char * bt_dev2node (char const *devname, char *nodename, int nnlen);
 
 int
+bt_devopen(char const *devname)
+{
+	struct sockaddr_hci	ha;
+	bdaddr_t		ba;
+	int			s;
+
+	if (devname == NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	memset(&ha, 0, sizeof(ha));
+	ha.hci_len = sizeof(ha);
+	ha.hci_family = AF_BLUETOOTH;
+
+	if (bt_aton(devname, &ba)) {
+		if (!bt_devname(ha.hci_node, &ba))
+			return (-1);
+	} else if (bt_dev2node(devname, ha.hci_node,
+					sizeof(ha.hci_node)) == NULL) {
+		errno = ENXIO;
+		return (-1);
+	}
+
+	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
+	if (s < 0)
+		return (-1);
+
+	if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 ||
+	    connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0) {
+		close(s);
+		return (-1);
+	}
+
+	return (s);
+}
+
+int
+bt_devclose(int s)
+{
+	return (close(s));
+}
+
+int
+bt_devsend(int s, uint16_t ogf, uint16_t ocf, int plen, void *param)
+{
+	ng_hci_cmd_pkt_t	h;
+	struct iovec		iv[2];
+	int			ivn;
+
+	if (plen < 0 || (plen > 0 && param == NULL)) { 
+		errno = EINVAL;
+		return (-1);
+	}
+
+	iv[0].iov_base = &h;
+	iv[0].iov_len = sizeof(h);
+	ivn = 1;
+
+	h.type = NG_HCI_CMD_PKT;
+	h.opcode = htole16(NG_HCI_OPCODE(ogf, ocf));
+	if (plen > 0) {
+		h.length = plen;
+
+		iv[1].iov_base = param;
+		iv[1].iov_len = plen;
+		ivn = 2;
+	} else
+		h.length = 0;
+
+	while (writev(s, iv, ivn) < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			continue;
+
+		return (-1);
+	}
+
+	return (0);
+}
+
+int
+bt_devrecv(int s, uint8_t *buf, int size, time_t to)
+{
+	fd_set		rfd;
+	struct timeval	tv;
+	int		n;
+
+	if (buf == NULL || size <= 0 || to < 0) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	FD_ZERO(&rfd);
+	FD_SET(s, &rfd);
+
+	tv.tv_sec = to;
+	tv.tv_usec = 0;
+
+	while ((n = select(s + 1, &rfd, NULL, NULL, &tv)) < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			continue;
+
+		return (-1);
+	}
+
+	if (n == 0) {
+		errno = ETIMEDOUT;
+		return (-1);
+	}
+
+	assert(FD_ISSET(s, &rfd));
+
+	while ((n = read(s, buf, size)) < 0) {
+		if (errno == EAGAIN || errno == EINTR)
+			continue;
+
+		return (-1);
+	}
+
+	return (n);
+}
+
+int
+bt_devreq(int s, struct bt_devreq *r, time_t to)
+{
+	uint8_t				buf[320]; /* more than enough */
+	ng_hci_event_pkt_t		*e = (ng_hci_event_pkt_t *) buf;
+	ng_hci_command_compl_ep		*cc = (ng_hci_command_compl_ep *)(e+1);
+	ng_hci_command_status_ep	*cs = (ng_hci_command_status_ep*)(e+1);
+	uint16_t			opcode;
+	time_t				t_end;
+	int				n;
+
+	if (s < 0 || r == NULL || to < 0) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	if (r->rlen < 0 || (r->rlen > 0 && r->rparam == NULL)) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	n = bt_devsend(s, r->ogf, r->ocf, r->clen, r->cparam);
+	if (n < 0)
+		return (-1);
+
+	opcode = htole16(NG_HCI_OPCODE(r->ogf, r->ocf));
+
+	t_end = time(NULL) + to;
+
+	do {
+		to = t_end - time(NULL);
+		if (to < 0)
+			to = 0;
+
+		n = bt_devrecv(s, buf, sizeof(buf), to);
+		if (n < 0)
+			return (-1);
+
+		if (n < sizeof(*e)) {
+			errno = EMSGSIZE;
+			return (-1);
+		}
+
+		if (e->type != NG_HCI_EVENT_PKT) {
+			errno = EIO;
+			return (-1);
+		}
+
+		n -= sizeof(*e);
+
+		switch (e->event) {
+		case NG_HCI_EVENT_COMMAND_COMPL:
+			if (cc->opcode == opcode) {
+				n -= sizeof(*cc);
+
+				if (r->rlen >= n) {
+					r->rlen = n;
+					memcpy(r->rparam, cc + 1, r->rlen);
+				}
+
+				return (0);
+			}
+			break;
+
+		case NG_HCI_EVENT_COMMAND_STATUS:
+			if (cs->opcode == opcode) {
+				if (r->event != NG_HCI_EVENT_COMMAND_STATUS) {
+					if (cs->status != 0) {
+						errno = EIO;
+						return (-1);
+					}
+				} else {
+					if (r->rlen >= n) {
+						r->rlen = n;
+						memcpy(r->rparam, cs, r->rlen);
+					}
+
+					return (0);
+				}
+			}
+			break;
+
+		default:
+			if (e->event == r->event) {
+				if (r->rlen >= n) {
+					r->rlen = n;
+					memcpy(r->rparam, e + 1, r->rlen);
+				}
+
+				return (0);
+			}
+			break;
+		}
+	} while (to > 0);
+
+	errno = ETIMEDOUT;
+
+	return (-1);
+}
+
+int
+bt_devfilter(int s, struct bt_devfilter const *new, struct bt_devfilter *old)
+{
+	struct ng_btsocket_hci_raw_filter	f;
+	socklen_t				len;
+	int					bit;
+
+	if (new == NULL && old == NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	if (old != NULL) {
+		len = sizeof(f);
+		if (getsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, &f, &len) < 0)
+			return (-1);
+
+		memset(old, 0, sizeof(*old));
+
+		for (bit = 0; bit < NG_HCI_EVENT_PKT; bit ++)
+			if (bit_test(f.packet_mask, bit))
+				old->packet_mask |= (1 << bit);
+
+		for (bit = 0; bit < NG_HCI_EVENT_MASK_SIZE * 8; bit ++)
+			if (bit_test(f.event_mask, bit))
+				old->event_mask |= (1 << bit);
+	}
+
+	if (new != NULL) {
+		memset(&f, 0, sizeof(f));
+
+		for (bit = 0; bit < NG_HCI_EVENT_PKT; bit ++)
+			if (new->packet_mask & (1 << bit))
+				bit_set(f.packet_mask, bit);
+
+		for (bit = 0; bit < (NG_HCI_EVENT_MASK_SIZE * 8); bit ++)
+			if (new->event_mask & (1 << bit))
+				bit_set(f.event_mask, bit);
+
+		len = sizeof(f);
+		if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, &f, len) < 0)
+			return (-1);
+	}
+
+	return (0);
+}
+
+int
+bt_devinquiry(char const *devname, int length, int num_rsp,
+		uint8_t const *lap, struct bt_devinquiry **ii)
+{
+	uint8_t				buf[320];
+	char				_devname[HCI_DEVNAME_SIZE];
+	struct bt_devfilter		f;
+	ng_hci_inquiry_cp		*cp = (ng_hci_inquiry_cp *) buf;
+	ng_hci_event_pkt_t		*e = (ng_hci_event_pkt_t *) buf;
+	ng_hci_inquiry_result_ep	*ep = (ng_hci_inquiry_result_ep *)(e+1);
+	ng_hci_inquiry_response		*ir;
+	struct bt_devinquiry		*i;
+	int				s, n;
+	time_t				to;
+
+	if (ii == NULL) {
+		errno = EINVAL;
+		return (-1);
+	}
+
+	if (devname == NULL) {
+		memset(_devname, 0, sizeof(_devname));
+		devname = _devname;
+
+		n = bt_devenum(bt_devany_cb, _devname);
+		if (n <= 0) {
+			if (n == 0)
+				*ii = NULL;
+
+			return (n);
+		}
+	}
+
+	s = bt_devopen(devname);
+	if (s < 0)
+		return (-1);
+
+	if (bt_devfilter(s, NULL, &f) < 0) {
+		bt_devclose(s);
+		return (-1);
+	}
+
+	f.event_mask |= (1 << (NG_HCI_EVENT_INQUIRY_COMPL - 1));
+	f.event_mask |= (1 << (NG_HCI_EVENT_INQUIRY_RESULT - 1));
+
+	if (bt_devfilter(s, &f, NULL) < 0) {
+		bt_devclose(s);
+		return (-1);
+	}
+
+	if (lap == NULL) {
+		cp->lap[0] = 0x33;
+		cp->lap[1] = 0x8b;
+		cp->lap[2] = 0x9e;
+	} else {
+		cp->lap[0] = lap[0];
+		cp->lap[1] = lap[1];
+		cp->lap[2] = lap[2];
+	}
+
+	if (length <= 0 || length > 255)
+		length = 4;	/* 5.12 seconds */
+	cp->inquiry_length = (uint8_t) length;
+
+	to = (time_t)((double) length * 1.28) + 1;
+
+	if (num_rsp <= 0 || num_rsp > 255)
+		num_rsp = 8;
+	cp->num_responses = (uint8_t) num_rsp;
+
+	i = *ii = calloc(num_rsp, sizeof(struct bt_devinquiry));
+	if (i == NULL) {
+		bt_devclose(s);
+		errno = ENOMEM;
+		return (-1);
+	}
+
+	if (bt_devsend(s, NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_INQUIRY,
+			sizeof(*cp), cp) < 0) {
+		free(i);
+		bt_devclose(s);
+		return (-1);
+	}
+
+wait_for_more:
+
+	n = bt_devrecv(s, buf, sizeof(buf), to);
+	if (n < 0) {
+		free(i);
+		bt_devclose(s);
+		return (-1);
+	}
+
+	if (n < sizeof(ng_hci_event_pkt_t)) {
+		free(i);
+		bt_devclose(s);
+		errno = EIO;
+		return (-1);
+	}
+
+	switch (e->event) {
+	case NG_HCI_EVENT_INQUIRY_COMPL:
+		break;
+
+	case NG_HCI_EVENT_INQUIRY_RESULT:
+		ir = (ng_hci_inquiry_response *)(ep + 1);
+
+#undef	MIN
+#define	MIN(a, b)	(((a) < (b))? (a) : (b))
+
+		for (n = 0; n < MIN(ep->num_responses, num_rsp); n ++) {
+			bdaddr_copy(&i->bdaddr, &ir->bdaddr);
+			i->pscan_rep_mode = ir->page_scan_rep_mode;
+			i->pscan_period_mode = ir->page_scan_period_mode;
+			i->pscan_mode = ir->page_scan_mode;
+			memcpy(i->dev_class, ir->uclass, sizeof(i->dev_class));
+			i->clock_offset = le16toh(ir->clock_offset);
+
+			ir ++;
+			i ++;
+			num_rsp --;
+		}
+		/* FALLTHROUGH */
+
+	default:
+		goto wait_for_more;
+		/* NOT REACHED */
+	}
+
+	bt_devclose(s);
+		
+	return (i - *ii);
+}
+
+int
 bt_devinfo(struct bt_devinfo *di)
 {
 	union {
@@ -53,6 +459,7 @@
 		struct ng_btsocket_hci_raw_node_debug		r8;
 	}						rp;
 	struct sockaddr_hci				ha;
+	socklen_t					halen;
 	int						s, rval;
 
 	if (di == NULL) {
@@ -60,27 +467,14 @@
 		return (-1);
 	}
 
-	memset(&ha, 0, sizeof(ha));
-	ha.hci_len = sizeof(ha);
-	ha.hci_family = AF_BLUETOOTH;
-
-	if (bt_aton(di->devname, &rp.r1.bdaddr)) {
-		if (!bt_devname(ha.hci_node, &rp.r1.bdaddr))
-			return (-1);
-	} else if (bt_dev2node(di->devname, ha.hci_node,
-					sizeof(ha.hci_node)) == NULL) {
-		errno = ENXIO;
-		return (-1);
-	}
-
-	s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
+	s = bt_devopen(di->devname);
 	if (s < 0)
 		return (-1);
 
 	rval = -1;
 
-	if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 ||
-	    connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0)
+	halen = sizeof(ha);
+	if (getsockname(s, (struct sockaddr *) &ha, &halen) < 0)
 		goto bad;
 	strlcpy(di->devname, ha.hci_node, sizeof(di->devname));
 
@@ -138,7 +532,7 @@
 
 	rval = 0;
 bad:
-	close(s);
+	bt_devclose(s);
 
 	return (rval);
 }
@@ -205,6 +599,13 @@
 	return (count);
 }
 
+static int
+bt_devany_cb(int s, struct bt_devinfo const *di, void *xdevname)
+{
+	strlcpy((char *) xdevname, di->devname, HCI_DEVNAME_SIZE);
+	return (1);
+}
+
 static char *
 bt_dev2node(char const *devname, char *nodename, int nnlen)
 {
Index: bluetooth.h
===================================================================
--- bluetooth.h	(revision 190594)
+++ bluetooth.h	(working copy)
@@ -39,6 +39,7 @@
 #include <sys/endian.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
+#include <sys/uio.h>
 #include <sys/un.h>
 #include <errno.h>
 #include <netdb.h>
@@ -46,6 +47,7 @@
 #include <netgraph/bluetooth/include/ng_hci.h>
 #include <netgraph/bluetooth/include/ng_l2cap.h>
 #include <netgraph/bluetooth/include/ng_btsocket.h>
+#include <time.h>
 
 __BEGIN_DECLS
 
@@ -129,11 +131,46 @@
 	uint8_t		_padding[20];	/* leave space for future additions */
 };
 
+struct bt_devreq
+{
+	uint16_t	ogf;
+	uint16_t	ocf;
+	int		event;
+	void		*cparam;
+	int		clen;
+	void		*rparam;
+	int		rlen;
+};
+
+struct bt_devfilter {
+	uint64_t	event_mask;
+	uint8_t		packet_mask;
+};
+
+struct bt_devinquiry {
+	bdaddr_t	bdaddr;
+	uint8_t		pscan_rep_mode;
+	uint8_t		pscan_period_mode;
+	uint8_t		pscan_mode;
+	uint8_t		dev_class[3];
+	uint16_t	clock_offset;
+};
+
 typedef int	(bt_devenum_cb_t)(int, struct bt_devinfo const *, void *);
 
+int		bt_devopen (char const *devname);
+int		bt_devclose(int s);
+int		bt_devsend (int s, uint16_t ogf, uint16_t ocf, int plen, void *param);
+int		bt_devrecv (int s, uint8_t *buf, int size, time_t to);
+int		bt_devreq  (int s, struct bt_devreq *r, time_t to);
+int		bt_devfilter(int s, struct bt_devfilter const *new,
+			     struct bt_devfilter *old);
+int		bt_devinquiry(char const *devname, int length, int num_rsp,
+			      uint8_t const *lap, struct bt_devinquiry **ii);
 int		bt_devinfo (struct bt_devinfo *di);
 int		bt_devenum (bt_devenum_cb_t *cb, void *arg);
 
+
 /*
  * bdaddr utility functions (from NetBSD)
  */


More information about the freebsd-bluetooth mailing list