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
9e8894a010
commit
78b96635e6
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user