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:
parent
2afddc7474
commit
4eabe37bdd
@ -33,6 +33,19 @@ MLINKS+= bluetooth.3 bt_devname.3
|
||||
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_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_any.3
|
||||
MLINKS+= bluetooth.3 bdaddr_copy.3
|
||||
|
@ -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,23 @@
|
||||
.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_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_any ,
|
||||
.Nm bdaddr_copy
|
||||
@ -84,6 +101,32 @@
|
||||
.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 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"
|
||||
.Ft int
|
||||
.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.
|
||||
.Pp
|
||||
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_any
|
||||
and
|
||||
@ -444,6 +721,6 @@ will be bound and connected to the Bluetooth device being enumerated.
|
||||
.Sh AUTHORS
|
||||
.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
|
||||
.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
|
||||
copied before any subsequent calls overwrite it.
|
||||
|
@ -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,48 @@ struct bt_devinfo
|
||||
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 *);
|
||||
|
||||
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_devenum (bt_devenum_cb_t *cb, void *arg);
|
||||
|
||||
|
@ -30,14 +30,504 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <bluetooth.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.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);
|
||||
|
||||
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
|
||||
bt_devinfo(struct bt_devinfo *di)
|
||||
{
|
||||
@ -53,6 +543,7 @@ bt_devinfo(struct bt_devinfo *di)
|
||||
struct ng_btsocket_hci_raw_node_debug r8;
|
||||
} rp;
|
||||
struct sockaddr_hci ha;
|
||||
socklen_t halen;
|
||||
int s, rval;
|
||||
|
||||
if (di == NULL) {
|
||||
@ -60,27 +551,14 @@ bt_devinfo(struct bt_devinfo *di)
|
||||
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 +616,7 @@ bt_devinfo(struct bt_devinfo *di)
|
||||
|
||||
rval = 0;
|
||||
bad:
|
||||
close(s);
|
||||
bt_devclose(s);
|
||||
|
||||
return (rval);
|
||||
}
|
||||
@ -205,6 +683,13 @@ bt_devenum(bt_devenum_cb_t cb, void *arg)
|
||||
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)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user