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