Implement low-level Bluetooth HCI API.

This should make it easier to make Linux BlueZ libhci port.

Reviewed by:	Iain Hibbert < plunky -at- rya-online -dot- net > of NetBSD
MFC after:	1 week
Inspired by:	Linux BlueZ
Inspired by:	NetBSD
This commit is contained in:
Maksim Yevmenkin 2009-04-22 15:50:03 +00:00
parent 9e8894a010
commit 78b96635e6
4 changed files with 836 additions and 19 deletions

View File

@ -33,6 +33,19 @@ MLINKS+= bluetooth.3 bt_devname.3
MLINKS+= bluetooth.3 bt_devinfo.3 MLINKS+= bluetooth.3 bt_devinfo.3
MLINKS+= bluetooth.3 bt_devenum.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_devfilter_pkt_set.3
MLINKS+= bluetooth.3 bt_devfilter_pkt_clr.3
MLINKS+= bluetooth.3 bt_devfilter_pkt_tst.3
MLINKS+= bluetooth.3 bt_devfilter_evt_set.3
MLINKS+= bluetooth.3 bt_devfilter_evt_clr.3
MLINKS+= bluetooth.3 bt_devfilter_evt_tst.3
MLINKS+= bluetooth.3 bt_devinquiry.3
MLINKS+= bluetooth.3 bdaddr_same.3 MLINKS+= bluetooth.3 bdaddr_same.3
MLINKS+= bluetooth.3 bdaddr_any.3 MLINKS+= bluetooth.3 bdaddr_any.3
MLINKS+= bluetooth.3 bdaddr_copy.3 MLINKS+= bluetooth.3 bdaddr_copy.3

View File

@ -25,7 +25,7 @@
.\" $Id: bluetooth.3,v 1.5 2003/05/20 23:04:30 max Exp $ .\" $Id: bluetooth.3,v 1.5 2003/05/20 23:04:30 max Exp $
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd February 13, 2009 .Dd April 9, 2009
.Dt BLUETOOTH 3 .Dt BLUETOOTH 3
.Os .Os
.Sh NAME .Sh NAME
@ -41,6 +41,23 @@
.Nm bt_endprotoent , .Nm bt_endprotoent ,
.Nm bt_aton , .Nm bt_aton ,
.Nm bt_ntoa , .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_devfilter_pkt_set ,
.Nm bt_devfilter_pkt_clr ,
.Nm bt_devfilter_pkt_tst ,
.Nm bt_devfilter_evt_set ,
.Nm bt_devfilter_evt_clr ,
.Nm bt_devfilter_evt_tst ,
.Nm bt_devinquiry ,
.Nm bdaddr_same , .Nm bdaddr_same ,
.Nm bdaddr_any , .Nm bdaddr_any ,
.Nm bdaddr_copy .Nm bdaddr_copy
@ -84,6 +101,32 @@
.Ft int .Ft int
.Fn bt_devenum "bt_devenum_cb_t *cb" "void *arg" .Fn bt_devenum "bt_devenum_cb_t *cb" "void *arg"
.Ft int .Ft int
.Fn bt_devopen "char const *devname"
.Ft int
.Fn bt_devclose "int s"
.Ft int
.Fn bt_devsend "int s" "uint16_t opcode" "void *param" "size_t plen"
.Ft ssize_t
.Fn bt_devrecv "int s" "void *buf" "size_t 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 void
.Fn bt_devfilter_pkt_set "struct bt_devfilter *filter" "uint8_t type"
.Ft void
.Fn bt_devfilter_pkt_clt "struct bt_devfilter *filter" "uint8_t type"
.Ft int
.Fn bt_devfilter_pkt_tst "struct bt_devfilter const *filter" "uint8_t type"
.Ft void
.Fn bt_devfilter_evt_set "struct bt_devfilter *filter" "uint8_t event"
.Ft void
.Fn bt_devfilter_evt_clt "struct bt_devfilter *filter" "uint8_t event"
.Ft int
.Fn bt_devfilter_evt_tst "struct bt_devfilter const *filter" "uint8_t event"
.Ft int
.Fn bt_devinquiry "char const *devname" "time_t length" "int num_rsp" "struct bt_devinquiry **ii"
.Ft int
.Fn bdaddr_same "const bdaddr_t *a" "const bdaddr_t *b" .Fn bdaddr_same "const bdaddr_t *a" "const bdaddr_t *b"
.Ft int .Ft int
.Fn bdaddr_any "const bdaddr_t *a" .Fn bdaddr_any "const bdaddr_t *a"
@ -311,6 +354,240 @@ The function returns number of successfully enumerated devices,
or -1 if an error occurred. or -1 if an error occurred.
.Pp .Pp
The The
.Fn bt_devopen
function opens a Bluetooth device with the given
.Fa devname
and returns a connected and bound
.Dv HCI
socket handle.
The function returns -1 if an error has occurred.
.Pp
The
.Fn bt_devclose
closes the passed
.Dv HCI
socket handle
.Fa s ,
previously obtained with
.Xr bt_devopen 3 .
.Pp
The
.Fn bt_devsend
function sends a Bluetooth
.Dv HCI
command with the given
.Fa opcode
to the provided socket
.Fa s ,
previously obtained with
.Xr bt_devopen 3 .
The
.Fa opcode
parameter is exppected to be in the host byte order.
The
.Fa param
and
.Fa plen
parameters specify command parameters.
The
.Fn bt_devsend
function does not modify the
.Dv HCI
filter on the provided socket
.Fa s .
The function returns 0 on success,
or -1 if an error occurred.
.Pp
The
.Fn bt_devrecv
function receives one Bluetooth
.Dv HCI
packet from the socket
.Fa s ,
previously obtained with
.Xr bt_devopen 3 .
The packet is placed into the provided buffer
.Fa buf
of size
.Fa size .
The
.Fa to
parameter specifies receive timeout in seconds.
Infinite timeout can be specified by passing negative value in the
.Fa to
parameter.
The
.Fn bt_devrecv
function does not modify the
.Dv HCI
filter on the provided socket
.Fa s .
The function returns total number of bytes recevied,
or -1 if an error occurred.
.Pp
The
.Fn bt_devreq
function makes a 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 opcode;
uint8_t event;
void *cparam;
size_t clen;
void *rparam;
size_t rlen;
};
.Ed
.Pp
The
.Fa opcode
field specifies the command and is expected to be in the host byte order.
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, otherwise it should be set to zero.
The
.Dv HCI
Command Complete and Command Status events are enabled by default.
The
.Fa rparam
and
.Fa rlen
parameters specify buffer and buffer size respectively where return
parameters should be placed.
The
.Fn bt_devreq
function temporarily modifies filter on the provided
.Dv HCI
socket
.Fa s .
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 ,
command and event packets, and, in addition, on
.Dv HCI
event IDs.
Before applying the
.Fa new
filter (if provided) the function will try to obtain the current filter
from the socket
.Fa s
and place it into the
.Fa old
parameter (if provided).
The function returns 0 on success, or -1 if an error occurred.
.Pp
The
.Fn bt_devfilter_pkt_set ,
.Fn bt_devfilter_pkt_clr
and
.Fn bt_devfilter_pkt_tst
functions can be used to modify and test the
.Dv HCI
filter
.Fa filter .
The
.Fa type
parameter specifies
.Dv HCI
packet type.
.Pp
The
.Fn bt_devfilter_evt_set ,
.Fn bt_devfilter_evt_clr
and
.Fn bt_devfilter_evt_tst
functions can be used to modify and test the
.Dv HCI
event filter
.Fa filter .
The
.Fa event
parameter specifies
.Dv HCI
event ID.
.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 seconds.
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 ii
parameter specifies where to place inquiry results.
On success, the function will return total number of inquiry results,
will allocate,
using
.Xr calloc 3 ,
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 using
.Xr free 3
call.
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 dev_class[3];
uint16_t clock_offset;
int8_t rssi;
uint8_t data[240];
};
.Ed
.Pp
The
.Fn bdaddr_same , .Fn bdaddr_same ,
.Fn bdaddr_any .Fn bdaddr_any
and and
@ -444,6 +721,6 @@ will be bound and connected to the Bluetooth device being enumerated.
.Sh AUTHORS .Sh AUTHORS
.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com .An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
.Sh BUGS .Sh BUGS
These functions use static data storage; Some of those functions use static data storage;
if the data is needed for future use, it should be if the data is needed for future use, it should be
copied before any subsequent calls overwrite it. copied before any subsequent calls overwrite it.

View File

@ -39,6 +39,7 @@
#include <sys/endian.h> #include <sys/endian.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/uio.h>
#include <sys/un.h> #include <sys/un.h>
#include <errno.h> #include <errno.h>
#include <netdb.h> #include <netdb.h>
@ -46,6 +47,7 @@
#include <netgraph/bluetooth/include/ng_hci.h> #include <netgraph/bluetooth/include/ng_hci.h>
#include <netgraph/bluetooth/include/ng_l2cap.h> #include <netgraph/bluetooth/include/ng_l2cap.h>
#include <netgraph/bluetooth/include/ng_btsocket.h> #include <netgraph/bluetooth/include/ng_btsocket.h>
#include <time.h>
__BEGIN_DECLS __BEGIN_DECLS
@ -129,8 +131,48 @@ struct bt_devinfo
uint8_t _padding[20]; /* leave space for future additions */ uint8_t _padding[20]; /* leave space for future additions */
}; };
struct bt_devreq
{
uint16_t opcode;
uint8_t event;
void *cparam;
size_t clen;
void *rparam;
size_t rlen;
};
struct bt_devfilter {
bitstr_t bit_decl(packet_mask, 8);
bitstr_t bit_decl(event_mask, 256);
};
struct bt_devinquiry {
bdaddr_t bdaddr;
uint8_t pscan_rep_mode;
uint8_t pscan_period_mode;
uint8_t dev_class[3];
uint16_t clock_offset;
int8_t rssi;
uint8_t data[240];
};
typedef int (bt_devenum_cb_t)(int, struct bt_devinfo const *, void *); 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 opcode, void *param, size_t plen);
ssize_t bt_devrecv (int s, void *buf, size_t 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);
void bt_devfilter_pkt_set(struct bt_devfilter *filter, uint8_t type);
void bt_devfilter_pkt_clr(struct bt_devfilter *filter, uint8_t type);
int bt_devfilter_pkt_tst(struct bt_devfilter const *filter, uint8_t type);
void bt_devfilter_evt_set(struct bt_devfilter *filter, uint8_t event);
void bt_devfilter_evt_clr(struct bt_devfilter *filter, uint8_t event);
int bt_devfilter_evt_tst(struct bt_devfilter const *filter, uint8_t event);
int bt_devinquiry(char const *devname, time_t length, int num_rsp,
struct bt_devinquiry **ii);
int bt_devinfo (struct bt_devinfo *di); int bt_devinfo (struct bt_devinfo *di);
int bt_devenum (bt_devenum_cb_t *cb, void *arg); int bt_devenum (bt_devenum_cb_t *cb, void *arg);

View File

@ -30,14 +30,504 @@
* $FreeBSD$ * $FreeBSD$
*/ */
#include <assert.h>
#include <bluetooth.h> #include <bluetooth.h>
#include <inttypes.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#undef MIN
#define MIN(a, b) (((a) < (b))? (a) : (b))
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); 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 opcode, void *param, size_t plen)
{
ng_hci_cmd_pkt_t h;
struct iovec iv[2];
int ivn;
if ((plen == 0 && param != NULL) ||
(plen > 0 && param == NULL) ||
plen > UINT8_MAX) {
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(opcode);
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);
}
ssize_t
bt_devrecv(int s, void *buf, size_t size, time_t to)
{
ssize_t n;
if (buf == NULL || size == 0) {
errno = EINVAL;
return (-1);
}
if (to >= 0) {
fd_set rfd;
struct timeval tv;
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);
}
switch (*((uint8_t *) buf)) {
case NG_HCI_CMD_PKT: {
ng_hci_cmd_pkt_t *h = (ng_hci_cmd_pkt_t *) buf;
if (n >= sizeof(*h) && n == (sizeof(*h) + h->length))
return (n);
} break;
case NG_HCI_ACL_DATA_PKT: {
ng_hci_acldata_pkt_t *h = (ng_hci_acldata_pkt_t *) buf;
if (n >= sizeof(*h) && n == (sizeof(*h) + le16toh(h->length)))
return (n);
} break;
case NG_HCI_SCO_DATA_PKT: {
ng_hci_scodata_pkt_t *h = (ng_hci_scodata_pkt_t *) buf;
if (n >= sizeof(*h) && n == (sizeof(*h) + h->length))
return (n);
} break;
case NG_HCI_EVENT_PKT: {
ng_hci_event_pkt_t *h = (ng_hci_event_pkt_t *) buf;
if (n >= sizeof(*h) && n == (sizeof(*h) + h->length))
return (n);
} break;
}
errno = EIO;
return (-1);
}
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);
struct bt_devfilter old, new;
time_t t_end;
uint16_t opcode;
ssize_t n;
int error;
if (s < 0 || r == NULL || to < 0) {
errno = EINVAL;
return (-1);
}
if ((r->rlen == 0 && r->rparam != NULL) ||
(r->rlen > 0 && r->rparam == NULL)) {
errno = EINVAL;
return (-1);
}
memset(&new, 0, sizeof(new));
bt_devfilter_pkt_set(&new, NG_HCI_EVENT_PKT);
bt_devfilter_evt_set(&new, NG_HCI_EVENT_COMMAND_COMPL);
bt_devfilter_evt_set(&new, NG_HCI_EVENT_COMMAND_STATUS);
if (r->event != 0)
bt_devfilter_evt_set(&new, r->event);
if (bt_devfilter(s, &new, &old) < 0)
return (-1);
error = 0;
n = bt_devsend(s, r->opcode, r->cparam, r->clen);
if (n < 0) {
error = errno;
goto out;
}
opcode = htole16(r->opcode);
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) {
error = errno;
goto out;
}
if (e->type != NG_HCI_EVENT_PKT) {
error = EIO;
goto out;
}
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);
}
goto out;
}
break;
case NG_HCI_EVENT_COMMAND_STATUS:
if (cs->opcode == opcode) {
if (r->event != NG_HCI_EVENT_COMMAND_STATUS) {
if (cs->status != 0) {
error = EIO;
goto out;
}
} else {
if (r->rlen >= n) {
r->rlen = n;
memcpy(r->rparam, cs, r->rlen);
}
goto out;
}
}
break;
default:
if (e->event == r->event) {
if (r->rlen >= n) {
r->rlen = n;
memcpy(r->rparam, e + 1, r->rlen);
}
goto out;
}
break;
}
} while (to > 0);
error = ETIMEDOUT;
out:
bt_devfilter(s, &old, NULL);
if (error != 0) {
errno = error;
return (-1);
}
return (0);
}
int
bt_devfilter(int s, struct bt_devfilter const *new, struct bt_devfilter *old)
{
struct ng_btsocket_hci_raw_filter f;
socklen_t len;
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));
memcpy(old->packet_mask, &f.packet_mask,
MIN(sizeof(old->packet_mask), sizeof(f.packet_mask)));
memcpy(old->event_mask, &f.event_mask,
MIN(sizeof(old->event_mask), sizeof(f.packet_mask)));
}
if (new != NULL) {
memset(&f, 0, sizeof(f));
memcpy(&f.packet_mask, new->packet_mask,
MIN(sizeof(f.packet_mask), sizeof(new->event_mask)));
memcpy(&f.event_mask, new->event_mask,
MIN(sizeof(f.event_mask), sizeof(new->event_mask)));
len = sizeof(f);
if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER, &f, len) < 0)
return (-1);
}
return (0);
}
void
bt_devfilter_pkt_set(struct bt_devfilter *filter, uint8_t type)
{
bit_set(filter->packet_mask, type - 1);
}
void
bt_devfilter_pkt_clr(struct bt_devfilter *filter, uint8_t type)
{
bit_clear(filter->packet_mask, type - 1);
}
int
bt_devfilter_pkt_tst(struct bt_devfilter const *filter, uint8_t type)
{
return (bit_test(filter->packet_mask, type - 1));
}
void
bt_devfilter_evt_set(struct bt_devfilter *filter, uint8_t event)
{
bit_set(filter->event_mask, event - 1);
}
void
bt_devfilter_evt_clr(struct bt_devfilter *filter, uint8_t event)
{
bit_clear(filter->event_mask, event - 1);
}
int
bt_devfilter_evt_tst(struct bt_devfilter const *filter, uint8_t event)
{
return (bit_test(filter->event_mask, event - 1));
}
int
bt_devinquiry(char const *devname, time_t length, int num_rsp,
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);
}
bt_devfilter_evt_set(&f, NG_HCI_EVENT_INQUIRY_COMPL);
bt_devfilter_evt_set(&f, NG_HCI_EVENT_INQUIRY_RESULT);
if (bt_devfilter(s, &f, NULL) < 0) {
bt_devclose(s);
return (-1);
}
/* Always use GIAC LAP */
cp->lap[0] = 0x33;
cp->lap[1] = 0x8b;
cp->lap[2] = 0x9e;
/* Calculate inquire length in 1.28 second units */
to = (time_t) ((double) length / 1.28);
if (to <= 0)
cp->inquiry_length = 4; /* 5.12 seconds */
else if (to > 254)
cp->inquiry_length = 255; /* 326.40 seconds */
else
cp->inquiry_length = to + 1;
to = (time_t)((double) cp->inquiry_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_OPCODE(NG_HCI_OGF_LINK_CONTROL, NG_HCI_OCF_INQUIRY),
cp, sizeof(*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);
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;
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 int
bt_devinfo(struct bt_devinfo *di) bt_devinfo(struct bt_devinfo *di)
{ {
@ -53,6 +543,7 @@ bt_devinfo(struct bt_devinfo *di)
struct ng_btsocket_hci_raw_node_debug r8; struct ng_btsocket_hci_raw_node_debug r8;
} rp; } rp;
struct sockaddr_hci ha; struct sockaddr_hci ha;
socklen_t halen;
int s, rval; int s, rval;
if (di == NULL) { if (di == NULL) {
@ -60,27 +551,14 @@ bt_devinfo(struct bt_devinfo *di)
return (-1); return (-1);
} }
memset(&ha, 0, sizeof(ha)); s = bt_devopen(di->devname);
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);
if (s < 0) if (s < 0)
return (-1); return (-1);
rval = -1; rval = -1;
if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || halen = sizeof(ha);
connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0) if (getsockname(s, (struct sockaddr *) &ha, &halen) < 0)
goto bad; goto bad;
strlcpy(di->devname, ha.hci_node, sizeof(di->devname)); strlcpy(di->devname, ha.hci_node, sizeof(di->devname));
@ -138,7 +616,7 @@ bt_devinfo(struct bt_devinfo *di)
rval = 0; rval = 0;
bad: bad:
close(s); bt_devclose(s);
return (rval); return (rval);
} }
@ -205,6 +683,13 @@ bt_devenum(bt_devenum_cb_t cb, void *arg)
return (count); 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 * static char *
bt_dev2node(char const *devname, char *nodename, int nnlen) bt_dev2node(char const *devname, char *nodename, int nnlen)
{ {