libhci update
Maksim Yevmenkin
maksim.yevmenkin at gmail.com
Wed Apr 15 13:59:36 PDT 2009
dear freebsd-bluetoth@ users,
please find attached patch that implements more compatibility shims.
any comments are greatly appreciated.
thanks
max
-------------- next part --------------
Index: hci.c
===================================================================
--- hci.c (revision 190870)
+++ 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.3
===================================================================
--- bluetooth.3 (revision 190870)
+++ bluetooth.3 (working copy)
@@ -25,7 +25,7 @@
.\" $Id: bluetooth.3,v 1.5 2003/05/20 23:04:30 max Exp $
.\" $FreeBSD$
.\"
-.Dd February 13, 2009
+.Dd April 9, 2009
.Dt BLUETOOTH 3
.Os
.Sh NAME
@@ -41,6 +41,17 @@
.Nm bt_endprotoent ,
.Nm bt_aton ,
.Nm bt_ntoa ,
+.Nm bt_devaddr ,
+.Nm bt_devname ,
+.Nm bt_devinfo ,
+.Nm bt_devenum ,
+.Nm bt_devopen ,
+.Nm bt_devclose ,
+.Nm bt_devsend ,
+.Nm bt_devrecv ,
+.Nm bt_devreq ,
+.Nm bt_devfilter ,
+.Nm bt_devinquiry ,
.Nm bdaddr_same ,
.Nm bdaddr_any ,
.Nm bdaddr_copy
@@ -84,6 +95,20 @@
.Ft int
.Fn bt_devenum "bt_devenum_cb_t *cb" "void *arg"
.Ft int
+.Fn bt_devopen "char const *devname"
+.Ft int
+.Fn bt_devclose "int s"
+.Ft int
+.Fn bt_devsend "int s" "uint16_t ogf" "uint16_t ocf" "int plen" "void *param"
+.Ft int
+.Fn bt_devrecv "int s" "uint8_t *buf" "int size" "time_t to"
+.Ft int
+.Fn bt_devreq "int s" "struct bt_devreq *r" "time_t to"
+.Ft int
+.Fn bt_devfilter "int s" "struct bt_devfilter const *new" "struct bt_devfilter *old"
+.Ft int
+.Fn bt_devinquiry "char const *devname" "int length" "int num_rsp" "uint8_t const *lap" "struct bt_devinquiry **ii"
+.Ft int
.Fn bdaddr_same "const bdaddr_t *a" "const bdaddr_t *b"
.Ft int
.Fn bdaddr_any "const bdaddr_t *a"
@@ -311,6 +336,219 @@
or -1 if an error occurred.
.Pp
The
+.Fn bt_devopen
+function opens Bluetooth device with the given
+.Fa devname
+and returns connected and bound
+.Dv HCI
+socket.
+The function returns -1 if an error has occurred.
+.Pp
+The
+.Fn bt_devclose
+closes passed
+.Dv HCI
+socket
+.Fa s ,
+previously obtained with
+.Xr bt_devopen 3 .
+.Pp
+The
+.Fn bt_devsend
+function sends Bluetooth
+.Dv HCI
+command with the OpCode Group Field
+.Fa ogf
+and
+OpCode Command Field
+.Fa ocf
+to the provided socket
+.Fa s ,
+previously obtained with
+.Xr bt_devopen 3 .
+The
+.Fa plen
+and
+.Fa param
+parameters specify command parameters.
+The function returns 0 on success,
+or -1 if an error occurred.
+.Pp
+The
+.Fn bt_devrecv
+function receives one Bluetooth
+.Dv HCI
+event packet from the socket
+.Fa s ,
+previously obtained with
+.Xr bt_devopen 3 .
+The event packet is placed into the provided buffer
+.Fa buf
+of size
+.Fa size .
+The
+.Fa to
+parameter specifies receive timeout in seconds.
+The function returns total number of bytes recevied,
+or -1 if an error occurred.
+.Pp
+The
+.Fn bt_devreq
+function makes Bluetooth
+.Dv HCI
+request to the socket
+.Fa s ,
+previously obtained with
+.Xr bt_devopen 3 .
+The function will send the specified command and will wait for the specified
+event,
+or timeout
+.Fa to
+seconds to occur.
+The
+.Vt bt_devreq
+structure is defined as follows
+.Bd -literal -offset indent
+struct bt_devreq
+{
+ uint16_t ogf;
+ uint16_t ocf;
+ int event;
+ void *cparam;
+ int clen;
+ void *rparam;
+ int rlen;
+};
+.Ed
+.Pp
+The
+.Fa ogf
+and
+.Fa ocf
+fields specify OpCode Group and Command Field respectively.
+The
+.Fa cparam
+and
+.Fa clen
+fields specify command parameters data and command parameters data size
+respectively.
+The
+.Fa event
+field specifies which Bluetooth
+.Dv HCI
+event ID the function should wait for.
+The
+.Fa rparam
+and
+.Fa rlen
+parameters specify buffer and buffer size respectively where return
+parameters should be placed.
+The function returns 0 on success, or -1 if an error occurred.
+.Pp
+The
+.Fn bt_devfilter
+controls the local
+.Dv HCI
+filter associated with the socket
+.Fa s ,
+previously obtained with
+.Xr bt_devopen 3 .
+Filtering can be done on packet types, i.e.
+.Dv ACL ,
+.Dv SCO or
+.Dv HCI
+event packets, and, in addition, on
+.Dv HCI
+event IDs.
+Before applying
+.Fa new
+filter (if provided) the function will try to obtain current filter
+from the socket
+.Fa s
+and place it into the
+.Fa old
+parameter (if provided).
+The
+.Vt bt_devfilter
+structure is defined as follows
+.Bd -literal -offset indent
+struct bt_devfilter {
+ uint64_t event_mask;
+ uint8_t packet_mask;
+};
+.Ed
+.Pp
+Both
+.Fa event_mask
+and
+.Fa packet_mask
+fields are bit masks.
+If a bit
+.Fa N
+is cleared in the
+.Fa event_mask
+then the corresponding Bluetooth
+.Dv HCI
+event ID
+.Fa N
+is filtered out.
+If a bit
+.Fa N
+is cleared in the
+.Fa packet_mask
+then all the packets with the corresponding packet indicator are filtered out.
+The function returns 0 on success, or -1 if an error occurred.
+.Pp
+The
+.Fn bt_devinquiry
+function performs Bluetooth inquiry.
+The
+.Fa devname
+parameter specifies which local Bluetooth device should perform an inquiry.
+If not secified, i.e.
+.Dv NULL ,
+then first available device will be used.
+The
+.Fa length
+parameters specifies the total length of an inquiry in 1.28 second units.
+If not specified, i.e. 0, default value will be used.
+The
+.Fa num_rsp
+parameter specifies the number of responses that can be received before
+the inquiry is halted.
+If not specified, i.e. 0, default value will be used.
+The
+.Fa lap
+parameter contains the LAP from which the inquiry access code will be
+be derived when the inquiry procedure is made.
+If not specified, i.e.
+.Dv NULL ,
+then GIAC LAP 9e:8b:33 will be used.
+The
+.Fa ii
+parameter specifies where to place inquiry results.
+On success, the function will return total number of inquiry results,
+will allocate buffer to store all the inquiry results and
+will return pointer to the allocated buffer in the
+.Fa ii
+parameter.
+It is up to the caller of the function to dispose of the buffer.
+The function returns -1 if an error has occurred.
+The
+.Vt bt_devinquiry
+structure is defined as follows
+.Bd -literal -offset indent
+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;
+};
+.Ed
+.Pp
+The
.Fn bdaddr_same ,
.Fn bdaddr_any
and
Index: bluetooth.h
===================================================================
--- bluetooth.h (revision 190870)
+++ 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,8 +131,42 @@
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);
Index: Makefile
===================================================================
--- Makefile (revision 190870)
+++ Makefile (working copy)
@@ -33,6 +33,13 @@
MLINKS+= bluetooth.3 bt_devinfo.3
MLINKS+= bluetooth.3 bt_devenum.3
+MLINKS+= bluetooth.3 bt_devopen.3
+MLINKS+= bluetooth.3 bt_devclose.3
+MLINKS+= bluetooth.3 bt_devsend.3
+MLINKS+= bluetooth.3 bt_devreq.3
+MLINKS+= bluetooth.3 bt_devfilter.3
+MLINKS+= bluetooth.3 bt_devinquiry.3
+
MLINKS+= bluetooth.3 bdaddr_same.3
MLINKS+= bluetooth.3 bdaddr_any.3
MLINKS+= bluetooth.3 bdaddr_copy.3
More information about the freebsd-bluetooth
mailing list