diff options
Diffstat (limited to 'lib/libbluetooth')
-rw-r--r-- | lib/libbluetooth/Makefile | 53 | ||||
-rw-r--r-- | lib/libbluetooth/bluetooth.3 | 726 | ||||
-rw-r--r-- | lib/libbluetooth/bluetooth.c | 371 | ||||
-rw-r--r-- | lib/libbluetooth/bluetooth.h | 212 | ||||
-rw-r--r-- | lib/libbluetooth/dev.c | 95 | ||||
-rw-r--r-- | lib/libbluetooth/hci.c | 734 |
6 files changed, 2191 insertions, 0 deletions
diff --git a/lib/libbluetooth/Makefile b/lib/libbluetooth/Makefile new file mode 100644 index 0000000..7e21892 --- /dev/null +++ b/lib/libbluetooth/Makefile @@ -0,0 +1,53 @@ +# $Id: Makefile,v 1.5 2003/07/22 18:38:04 max Exp $ +# $FreeBSD$ + +LIB= bluetooth +MAN= bluetooth.3 + +WARNS?= 2 +CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../../sys + +SHLIB_MAJOR= 4 + +SRCS= bluetooth.c dev.c hci.c +INCS= bluetooth.h + +MLINKS+= bluetooth.3 bt_gethostbyname.3 +MLINKS+= bluetooth.3 bt_gethostbyaddr.3 +MLINKS+= bluetooth.3 bt_gethostent.3 +MLINKS+= bluetooth.3 bt_sethostent.3 +MLINKS+= bluetooth.3 bt_endhostent.3 + +MLINKS+= bluetooth.3 bt_getprotobyname.3 +MLINKS+= bluetooth.3 bt_getprotobynumber.3 +MLINKS+= bluetooth.3 bt_getprotoent.3 +MLINKS+= bluetooth.3 bt_setprotoent.3 +MLINKS+= bluetooth.3 bt_endprotoent.3 + +MLINKS+= bluetooth.3 bt_ntoa.3 +MLINKS+= bluetooth.3 bt_aton.3 + +MLINKS+= bluetooth.3 bt_devaddr.3 +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 + +.include <bsd.lib.mk> diff --git a/lib/libbluetooth/bluetooth.3 b/lib/libbluetooth/bluetooth.3 new file mode 100644 index 0000000..cf7e3ed --- /dev/null +++ b/lib/libbluetooth/bluetooth.3 @@ -0,0 +1,726 @@ +.\" Copyright (c) 2003-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $Id: bluetooth.3,v 1.5 2003/05/20 23:04:30 max Exp $ +.\" $FreeBSD$ +.\" +.Dd April 9, 2009 +.Dt BLUETOOTH 3 +.Os +.Sh NAME +.Nm bt_gethostbyname , +.Nm bt_gethostbyaddr , +.Nm bt_gethostent , +.Nm bt_sethostent , +.Nm bt_endhostent , +.Nm bt_getprotobyname , +.Nm bt_getprotobynumber , +.Nm bt_getprotoent , +.Nm bt_setprotoent , +.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 +.Nd Bluetooth routines +.Sh LIBRARY +.Lb libbluetooth +.Sh SYNOPSIS +.In bluetooth.h +.Ft struct hostent * +.Fn bt_gethostbyname "const char *name" +.Ft struct hostent * +.Fn bt_gethostbyaddr "const char *addr" "int len" "int type" +.Ft struct hostent * +.Fn bt_gethostent void +.Ft void +.Fn bt_sethostent "int stayopen" +.Ft void +.Fn bt_endhostent void +.Ft struct protoent * +.Fn bt_getprotobyname "const char *name" +.Ft struct protoent * +.Fn bt_getprotobynumber "int proto" +.Ft struct protoent * +.Fn bt_getprotoent void +.Ft void +.Fn bt_setprotoent "int stayopen" +.Ft void +.Fn bt_endprotoent void +.Ft int +.Fn bt_aton "const char *str" "bdaddr_t *ba" +.Ft const char * +.Fn bt_ntoa "const bdaddr_t *ba" "char *str" +.Ft int +.Fn bt_devaddr "const char *devname" "bdaddr_t *addr" +.Ft int +.Fn bt_devname "char *devname" "const bdaddr_t *addr" +.Ft int +.Fn (bt_devenum_cb_t) "int s" "struct bt_devinfo const *di" "void *arg" +.Ft int +.Fn bt_devinfo "struct bt_devinfo *di" +.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" +.Ft int +.Fn bdaddr_copy "const bdaddr_t *dst" "const bdaddr_t *src" +.Sh DESCRIPTION +The +.Fn bt_gethostent , +.Fn bt_gethostbyname +and +.Fn bt_gethostbyaddr +functions +each return a pointer to an object with the +.Vt hostent +structure describing a Bluetooth host +referenced by name or by address, respectively. +.Pp +The +.Fa name +argument passed to +.Fn bt_gethostbyname +should point to a +.Dv NUL Ns -terminated +hostname. +The +.Fa addr +argument passed to +.Fn bt_gethostbyaddr +should point to an address which is +.Fa len +bytes long, +in binary form +(i.e., not a Bluetooth BD_ADDR in human readable +.Tn ASCII +form). +The +.Fa type +argument specifies the address family of this address and must be set to +.Dv AF_BLUETOOTH . +.Pp +The structure returned contains the information obtained from a line in +.Pa /etc/bluetooth/hosts +file. +.Pp +The +.Fn bt_sethostent +function controls whether +.Pa /etc/bluetooth/hosts +file should stay open after each call to +.Fn bt_gethostbyname +or +.Fn bt_gethostbyaddr . +If the +.Fa stayopen +flag is non-zero, the file will not be closed. +.Pp +The +.Fn bt_endhostent +function closes the +.Pa /etc/bluetooth/hosts +file. +.Pp +The +.Fn bt_getprotoent , +.Fn bt_getprotobyname +and +.Fn bt_getprotobynumber +functions each return a pointer to an object with the +.Vt protoent +structure describing a Bluetooth Protocol Service Multiplexor referenced +by name or number, respectively. +.Pp +The +.Fa name +argument passed to +.Fn bt_getprotobyname +should point to a +.Dv NUL Ns -terminated +Bluetooth Protocol Service Multiplexor name. +The +.Fa proto +argument passed to +.Fn bt_getprotobynumber +should have numeric value of the desired Bluetooth Protocol Service Multiplexor. +.Pp +The structure returned contains the information obtained from a line in +.Pa /etc/bluetooth/protocols +file. +.Pp +The +.Fn bt_setprotoent +function controls whether +.Pa /etc/bluetooth/protocols +file should stay open after each call to +.Fn bt_getprotobyname +or +.Fn bt_getprotobynumber . +If the +.Fa stayopen +flag is non-zero, the file will not be closed. +.Pp +The +.Fn bt_endprotoent +function closes the +.Pa /etc/bluetooth/protocols +file. +.Pp +The +.Fn bt_aton +routine interprets the specified character string as a Bluetooth address, +placing the address into the structure provided. +It returns 1 if the string was successfully interpreted, +or 0 if the string is invalid. +.Pp +The routine +.Fn bt_ntoa +takes a Bluetooth address and places an +.Tn ASCII +string representing the address into the buffer provided. +It is up to the caller to ensure that provided buffer has enough space. +If no buffer was provided then internal static buffer will be used. +.Pp +The +.Fn bt_devaddr +function interprets the specified +.Fa devname +string as the address or device name of a Bluetooth device on the local system, +and places the device address in the provided +.Fa bdaddr , +if any. +The function returns 1 if the string was successfully interpreted, +or 0 if the string did not match any local device. +The +.Fn bt_devname +function takes a Bluetooth device address and copies the local device +name associated with that address into the buffer provided, +if any. +Caller must ensure that provided buffer is at least +.Dv HCI_DEVNAME_SIZE +characters in size. +The function returns 1 when the device was found, +otherwise 0. +.Pp +The +.Fn bt_devinfo +function populates provided +.Vt bt_devinfo +structure with the information about given Bluetooth device. +The caller is expected to pass Bluetooth device name in the +.Fa devname +field of the passed +.Vt bt_devinfo +structure. +The function returns 0 when successful, +otherwise -1. +The +.Vt bt_devinfo +structure is defined as follows +.Bd -literal -offset indent +struct bt_devinfo +{ + char devname[HCI_DEVNAME_SIZE]; + + uint32_t state; + + bdaddr_t bdaddr; + uint16_t _reserved0; + + uint8_t features[HCI_DEVFEATURES_SIZE]; + + /* buffer info */ + uint16_t _reserved1; + uint16_t cmd_free; + uint16_t sco_size; + uint16_t sco_pkts; + uint16_t sco_free; + uint16_t acl_size; + uint16_t acl_pkts; + uint16_t acl_free; + + /* stats */ + uint32_t cmd_sent; + uint32_t evnt_recv; + uint32_t acl_recv; + uint32_t acl_sent; + uint32_t sco_recv; + uint32_t sco_sent; + uint32_t bytes_recv; + uint32_t bytes_sent; + + /* misc/specific */ + uint16_t link_policy_info; + uint16_t packet_type_info; + uint16_t role_switch_info; + uint16_t debug; + + uint8_t _padding[20]; +}; +.Ed +.Pp +The +.Fn bt_devenum +function enumerates Bluetooth devices present in the system. +For every device found, +the function will call provided +.Fa cb +callback function which should be of +.Vt bt_devenum_cb_t +type. +The callback function is passed a +.Dv HCI +socket +.Fa s , +fully populated +.Vt bt_devinfo +structure +.Fa di +and +.Fa arg +argument provided to the +.Fn bt_devenum . +The callback function can stop enumeration by returning a value +that is greater than zero. +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 expected 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 received, +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 specified, 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 +.Fn bdaddr_copy +are handy shorthand Bluetooth address utility functions. +The +.Fn bdaddr_same +function will test if two provided BD_ADDRs are the same. +The +.Fn bdaddr_any +function will test if provided BD_ADDR is +.Dv ANY +BD_ADDR. +The +.Fn bdaddr_copy +function will copy provided +.Fa src +BD_ADDR into provided +.Fa dst +BD_ADDR. +.Sh FILES +.Bl -tag -width ".Pa /etc/bluetooth/hosts" -compact +.It Pa /etc/bluetooth/hosts +.It Pa /etc/bluetooth/protocols +.El +.Sh EXAMPLES +Print out the hostname associated with a specific BD_ADDR: +.Bd -literal -offset indent +const char *bdstr = "00:01:02:03:04:05"; +bdaddr_t bd; +struct hostent *hp; + +if (!bt_aton(bdstr, &bd)) + errx(1, "can't parse BD_ADDR %s", bdstr); + +if ((hp = bt_gethostbyaddr((const char *)&bd, + sizeof(bd), AF_BLUETOOTH)) == NULL) + errx(1, "no name associated with %s", bdstr); + +printf("name associated with %s is %s\en", bdstr, hp->h_name); +.Ed +.Sh DIAGNOSTICS +Error return status from +.Fn bt_gethostent , +.Fn bt_gethostbyname +and +.Fn bt_gethostbyaddr +is indicated by return of a +.Dv NULL +pointer. +The external integer +.Va h_errno +may then be checked to see whether this is a temporary failure +or an invalid or unknown host. +The routine +.Xr herror 3 +can be used to print an error message describing the failure. +If its argument +.Fa string +is +.Pf non- Dv NULL , +it is printed, followed by a colon and a space. +The error message is printed with a trailing newline. +.Pp +The variable +.Va h_errno +can have the following values: +.Bl -tag -width ".Dv HOST_NOT_FOUND" +.It Dv HOST_NOT_FOUND +No such host is known. +.It Dv NO_RECOVERY +Some unexpected server failure was encountered. +This is a non-recoverable error. +.El +.Pp +The +.Fn bt_getprotoent , +.Fn bt_getprotobyname +and +.Fn bt_getprotobynumber +return +.Dv NULL +on EOF or error. +.Sh SEE ALSO +.Xr gethostbyaddr 3 , +.Xr gethostbyname 3 , +.Xr getprotobyname 3 , +.Xr getprotobynumber 3 , +.Xr herror 3 , +.Xr inet_aton 3 , +.Xr inet_ntoa 3 , +.Xr ng_hci 4 +.Sh CAVEAT +The +.Fn bt_gethostent +function reads the next line of +.Pa /etc/bluetooth/hosts , +opening the file if necessary. +.Pp +The +.Fn bt_sethostent +function opens and/or rewinds the +.Pa /etc/bluetooth/hosts +file. +.Pp +The +.Fn bt_getprotoent +function reads the next line of +.Pa /etc/bluetooth/protocols , +opening the file if necessary. +.Pp +The +.Fn bt_setprotoent +function opens and/or rewinds the +.Pa /etc/bluetooth/protocols +file. +.Pp +The +.Fn bt_devenum +function enumerates up to +.Dv HCI_DEVMAX +Bluetooth devices. +During enumeration the +.Fn bt_devenum +function uses the same +.Dv HCI +socket. +The function guarantees that the socket, +passed to the callback function, +will be bound and connected to the Bluetooth device being enumerated. +.Sh AUTHORS +.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com +.Sh BUGS +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. diff --git a/lib/libbluetooth/bluetooth.c b/lib/libbluetooth/bluetooth.c new file mode 100644 index 0000000..23b7df0 --- /dev/null +++ b/lib/libbluetooth/bluetooth.c @@ -0,0 +1,371 @@ +/* + * bluetooth.c + */ + +/*- + * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: bluetooth.c,v 1.3 2003/05/20 23:04:30 max Exp $ + * $FreeBSD$ + */ + +#include <bluetooth.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#define _PATH_BT_HOSTS "/etc/bluetooth/hosts" +#define _PATH_BT_PROTOCOLS "/etc/bluetooth/protocols" +#define MAXALIASES 35 + +static FILE *hostf = NULL; +static int host_stayopen = 0; +static struct hostent host; +static bdaddr_t host_addr; +static char *host_addr_ptrs[2]; +static char *host_aliases[MAXALIASES]; + +static FILE *protof = NULL; +static int proto_stayopen = 0; +static struct protoent proto; +static char *proto_aliases[MAXALIASES]; + +static char buf[BUFSIZ + 1]; + +static int bt_hex_byte (char const *str); +static int bt_hex_nibble (char nibble); + +struct hostent * +bt_gethostbyname(char const *name) +{ + struct hostent *p; + char **cp; + + bt_sethostent(host_stayopen); + while ((p = bt_gethostent()) != NULL) { + if (strcasecmp(p->h_name, name) == 0) + break; + for (cp = p->h_aliases; *cp != 0; cp++) + if (strcasecmp(*cp, name) == 0) + goto found; + } +found: + bt_endhostent(); + + return (p); +} + +struct hostent * +bt_gethostbyaddr(char const *addr, int len, int type) +{ + struct hostent *p; + + if (type != AF_BLUETOOTH || len != sizeof(bdaddr_t)) { + h_errno = NO_RECOVERY; + return (NULL); + } + + bt_sethostent(host_stayopen); + while ((p = bt_gethostent()) != NULL) + if (p->h_addrtype == type && bcmp(p->h_addr, addr, len) == 0) + break; + bt_endhostent(); + + return (p); +} + +struct hostent * +bt_gethostent(void) +{ + char *p, *cp, **q; + + if (hostf == NULL) + hostf = fopen(_PATH_BT_HOSTS, "r"); + + if (hostf == NULL) { + h_errno = NETDB_INTERNAL; + return (NULL); + } +again: + if ((p = fgets(buf, sizeof(buf), hostf)) == NULL) { + h_errno = HOST_NOT_FOUND; + return (NULL); + } + if (*p == '#') + goto again; + if ((cp = strpbrk(p, "#\n")) == NULL) + goto again; + *cp = 0; + if ((cp = strpbrk(p, " \t")) == NULL) + goto again; + *cp++ = 0; + if (bt_aton(p, &host_addr) == 0) + goto again; + host_addr_ptrs[0] = (char *) &host_addr; + host_addr_ptrs[1] = NULL; + host.h_addr_list = host_addr_ptrs; + host.h_length = sizeof(host_addr); + host.h_addrtype = AF_BLUETOOTH; + while (*cp == ' ' || *cp == '\t') + cp++; + host.h_name = cp; + q = host.h_aliases = host_aliases; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = 0; + while (cp != NULL && *cp != 0) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &host_aliases[MAXALIASES - 1]) + *q++ = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = 0; + } + *q = NULL; + h_errno = NETDB_SUCCESS; + + return (&host); +} + +void +bt_sethostent(int stayopen) +{ + if (hostf == NULL) + hostf = fopen(_PATH_BT_HOSTS, "r"); + else + rewind(hostf); + + host_stayopen = stayopen; +} + +void +bt_endhostent(void) +{ + if (hostf != NULL && host_stayopen == 0) { + (void) fclose(hostf); + hostf = NULL; + } +} + +struct protoent * +bt_getprotobyname(char const *name) +{ + struct protoent *p; + char **cp; + + bt_setprotoent(proto_stayopen); + while ((p = bt_getprotoent()) != NULL) { + if (strcmp(p->p_name, name) == 0) + break; + for (cp = p->p_aliases; *cp != 0; cp++) + if (strcmp(*cp, name) == 0) + goto found; + } +found: + bt_endprotoent(); + + return (p); +} + +struct protoent * +bt_getprotobynumber(int proto) +{ + struct protoent *p; + + bt_setprotoent(proto_stayopen); + while ((p = bt_getprotoent()) != NULL) + if (p->p_proto == proto) + break; + bt_endprotoent(); + + return (p); +} + +struct protoent * +bt_getprotoent(void) +{ + char *p, *cp, **q; + + if (protof == NULL) + protof = fopen(_PATH_BT_PROTOCOLS, "r"); + + if (protof == NULL) + return (NULL); +again: + if ((p = fgets(buf, sizeof(buf), protof)) == NULL) + return (NULL); + if (*p == '#') + goto again; + if ((cp = strpbrk(p, "#\n")) == NULL) + goto again; + *cp = '\0'; + proto.p_name = p; + if ((cp = strpbrk(p, " \t")) == NULL) + goto again; + *cp++ = '\0'; + while (*cp == ' ' || *cp == '\t') + cp++; + if ((p = strpbrk(cp, " \t")) != NULL) + *p++ = '\0'; + proto.p_proto = atoi(cp); + q = proto.p_aliases = proto_aliases; + if (p != NULL) { + cp = p; + while (cp != NULL && *cp != 0) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &proto_aliases[MAXALIASES - 1]) + *q++ = cp; + if ((cp = strpbrk(cp, " \t")) != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + + return (&proto); +} + +void +bt_setprotoent(int stayopen) +{ + if (protof == NULL) + protof = fopen(_PATH_BT_PROTOCOLS, "r"); + else + rewind(protof); + + proto_stayopen = stayopen; +} + +void +bt_endprotoent(void) +{ + if (protof != NULL) { + (void) fclose(protof); + protof = NULL; + } +} + +char const * +bt_ntoa(bdaddr_t const *ba, char *str) +{ + static char buffer[24]; + + if (str == NULL) + str = buffer; + + sprintf(str, "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", + ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]); + + return (str); +} + +int +bt_aton(char const *str, bdaddr_t *ba) +{ + int i, b; + char *end = NULL; + + memset(ba, 0, sizeof(*ba)); + + for (i = 5, end = strchr(str, ':'); + i > 0 && *str != '\0' && end != NULL; + i --, str = end + 1, end = strchr(str, ':')) { + switch (end - str) { + case 1: + b = bt_hex_nibble(str[0]); + break; + + case 2: + b = bt_hex_byte(str); + break; + + default: + b = -1; + break; + } + + if (b < 0) + return (0); + + ba->b[i] = b; + } + + if (i != 0 || end != NULL || *str == 0) + return (0); + + switch (strlen(str)) { + case 1: + b = bt_hex_nibble(str[0]); + break; + + case 2: + b = bt_hex_byte(str); + break; + + default: + b = -1; + break; + } + + if (b < 0) + return (0); + + ba->b[i] = b; + + return (1); +} + +static int +bt_hex_byte(char const *str) +{ + int n1, n2; + + if ((n1 = bt_hex_nibble(str[0])) < 0) + return (-1); + + if ((n2 = bt_hex_nibble(str[1])) < 0) + return (-1); + + return ((((n1 & 0x0f) << 4) | (n2 & 0x0f)) & 0xff); +} + +static int +bt_hex_nibble(char nibble) +{ + if ('0' <= nibble && nibble <= '9') + return (nibble - '0'); + + if ('a' <= nibble && nibble <= 'f') + return (nibble - 'a' + 0xa); + + if ('A' <= nibble && nibble <= 'F') + return (nibble - 'A' + 0xa); + + return (-1); +} + diff --git a/lib/libbluetooth/bluetooth.h b/lib/libbluetooth/bluetooth.h new file mode 100644 index 0000000..a73dbe9 --- /dev/null +++ b/lib/libbluetooth/bluetooth.h @@ -0,0 +1,212 @@ +/* + * bluetooth.h + */ + +/*- + * Copyright (c) 2001-2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $Id: bluetooth.h,v 1.5 2003/09/14 23:28:42 max Exp $ + * $FreeBSD$ + */ + +#ifndef _BLUETOOTH_H_ +#define _BLUETOOTH_H_ + +#include <sys/types.h> +#include <sys/bitstring.h> +#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> +#include <netgraph/ng_message.h> +#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 + +/* + * Linux BlueZ compatibility + */ + +#define bacmp(ba1, ba2) memcmp((ba1), (ba2), sizeof(bdaddr_t)) +#define bacpy(dst, src) memcpy((dst), (src), sizeof(bdaddr_t)) +#define ba2str(ba, str) bt_ntoa((ba), (str)) +#define str2ba(str, ba) (bt_aton((str), (ba)) == 1? 0 : -1) + +/* + * Interface to the outside world + */ + +struct hostent * bt_gethostbyname (char const *name); +struct hostent * bt_gethostbyaddr (char const *addr, int len, int type); +struct hostent * bt_gethostent (void); +void bt_sethostent (int stayopen); +void bt_endhostent (void); + +struct protoent * bt_getprotobyname (char const *name); +struct protoent * bt_getprotobynumber (int proto); +struct protoent * bt_getprotoent (void); +void bt_setprotoent (int stayopen); +void bt_endprotoent (void); + +char const * bt_ntoa (bdaddr_t const *ba, char *str); +int bt_aton (char const *str, bdaddr_t *ba); + +/* bt_devXXXX() functions (inspired by NetBSD) */ +int bt_devaddr (char const *devname, bdaddr_t *addr); +int bt_devname (char *devname, bdaddr_t const *addr); + +/* + * Bluetooth HCI functions + */ + +#define HCI_DEVMAX 32 /* arbitrary */ +#define HCI_DEVNAME_SIZE NG_NODESIZ +#define HCI_DEVFEATURES_SIZE NG_HCI_FEATURES_SIZE + +struct bt_devinfo +{ + char devname[HCI_DEVNAME_SIZE]; + + uint32_t state; /* device/implementation specific */ + + bdaddr_t bdaddr; + uint16_t _reserved0; + + uint8_t features[HCI_DEVFEATURES_SIZE]; + + /* buffer info */ + uint16_t _reserved1; + uint16_t cmd_free; + uint16_t sco_size; + uint16_t sco_pkts; + uint16_t sco_free; + uint16_t acl_size; + uint16_t acl_pkts; + uint16_t acl_free; + + /* stats */ + uint32_t cmd_sent; + uint32_t evnt_recv; + uint32_t acl_recv; + uint32_t acl_sent; + uint32_t sco_recv; + uint32_t sco_sent; + uint32_t bytes_recv; + uint32_t bytes_sent; + + /* misc/specific */ + uint16_t link_policy_info; + uint16_t packet_type_info; + uint16_t role_switch_info; + uint16_t debug; + + 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 *newp, + struct bt_devfilter *oldp); +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); + +/* + * bdaddr utility functions (from NetBSD) + */ + +static __inline int +bdaddr_same(const bdaddr_t *a, const bdaddr_t *b) +{ + return (a->b[0] == b->b[0] && a->b[1] == b->b[1] && + a->b[2] == b->b[2] && a->b[3] == b->b[3] && + a->b[4] == b->b[4] && a->b[5] == b->b[5]); +} + +static __inline int +bdaddr_any(const bdaddr_t *a) +{ + return (a->b[0] == 0 && a->b[1] == 0 && a->b[2] == 0 && + a->b[3] == 0 && a->b[4] == 0 && a->b[5] == 0); +} + +static __inline void +bdaddr_copy(bdaddr_t *d, const bdaddr_t *s) +{ + d->b[0] = s->b[0]; + d->b[1] = s->b[1]; + d->b[2] = s->b[2]; + d->b[3] = s->b[3]; + d->b[4] = s->b[4]; + d->b[5] = s->b[5]; +} + +__END_DECLS + +#endif /* ndef _BLUETOOTH_H_ */ + diff --git a/lib/libbluetooth/dev.c b/lib/libbluetooth/dev.c new file mode 100644 index 0000000..1f9e745 --- /dev/null +++ b/lib/libbluetooth/dev.c @@ -0,0 +1,95 @@ +/* + * dev.c + */ + +/*- + * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <bluetooth.h> +#include <stdio.h> +#include <string.h> + +struct bt_devaddr_match_arg +{ + char devname[HCI_DEVNAME_SIZE]; + bdaddr_t const *bdaddr; +}; + +static bt_devenum_cb_t bt_devaddr_match; + +int +bt_devaddr(char const *devname, bdaddr_t *addr) +{ + struct bt_devinfo di; + + strlcpy(di.devname, devname, sizeof(di.devname)); + + if (bt_devinfo(&di) < 0) + return (0); + + if (addr != NULL) + bdaddr_copy(addr, &di.bdaddr); + + return (1); +} + +int +bt_devname(char *devname, bdaddr_t const *addr) +{ + struct bt_devaddr_match_arg arg; + + memset(&arg, 0, sizeof(arg)); + arg.bdaddr = addr; + + if (bt_devenum(&bt_devaddr_match, &arg) < 0) + return (0); + + if (arg.devname[0] == '\0') { + errno = ENXIO; + return (0); + } + + if (devname != NULL) + strlcpy(devname, arg.devname, HCI_DEVNAME_SIZE); + + return (1); +} + +static int +bt_devaddr_match(int s, struct bt_devinfo const *di, void *arg) +{ + struct bt_devaddr_match_arg *m = (struct bt_devaddr_match_arg *)arg; + + if (!bdaddr_same(&di->bdaddr, m->bdaddr)) + return (0); + + strlcpy(m->devname, di->devname, sizeof(m->devname)); + + return (1); +} + diff --git a/lib/libbluetooth/hci.c b/lib/libbluetooth/hci.c new file mode 100644 index 0000000..1ae6ff9 --- /dev/null +++ b/lib/libbluetooth/hci.c @@ -0,0 +1,734 @@ +/* + * hci.c + */ + +/*- + * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $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; + + 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 + * v2.x specification says that 1.28 -> 61.44 seconds + * range is acceptable + */ + + if (length <= 0) + length = 5; + else if (length == 1) + length = 2; + else if (length > 62) + length = 62; + + cp->inquiry_length = (uint8_t)((length * 100) / 128); + + 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), length); + 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) +{ + union { + struct ng_btsocket_hci_raw_node_state r0; + struct ng_btsocket_hci_raw_node_bdaddr r1; + struct ng_btsocket_hci_raw_node_features r2; + struct ng_btsocket_hci_raw_node_buffer r3; + struct ng_btsocket_hci_raw_node_stat r4; + struct ng_btsocket_hci_raw_node_link_policy_mask r5; + struct ng_btsocket_hci_raw_node_packet_mask r6; + struct ng_btsocket_hci_raw_node_role_switch r7; + struct ng_btsocket_hci_raw_node_debug r8; + } rp; + struct sockaddr_hci ha; + socklen_t halen; + int s, rval; + + if (di == NULL) { + errno = EINVAL; + return (-1); + } + + s = bt_devopen(di->devname); + if (s < 0) + return (-1); + + rval = -1; + + halen = sizeof(ha); + if (getsockname(s, (struct sockaddr *) &ha, &halen) < 0) + goto bad; + strlcpy(di->devname, ha.hci_node, sizeof(di->devname)); + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &rp.r0, sizeof(rp.r0)) < 0) + goto bad; + di->state = rp.r0.state; + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &rp.r1, sizeof(rp.r1)) < 0) + goto bad; + bdaddr_copy(&di->bdaddr, &rp.r1.bdaddr); + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &rp.r2, sizeof(rp.r2)) < 0) + goto bad; + memcpy(di->features, rp.r2.features, sizeof(di->features)); + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &rp.r3, sizeof(rp.r3)) < 0) + goto bad; + di->cmd_free = rp.r3.buffer.cmd_free; + di->sco_size = rp.r3.buffer.sco_size; + di->sco_pkts = rp.r3.buffer.sco_pkts; + di->sco_free = rp.r3.buffer.sco_free; + di->acl_size = rp.r3.buffer.acl_size; + di->acl_pkts = rp.r3.buffer.acl_pkts; + di->acl_free = rp.r3.buffer.acl_free; + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &rp.r4, sizeof(rp.r4)) < 0) + goto bad; + di->cmd_sent = rp.r4.stat.cmd_sent; + di->evnt_recv = rp.r4.stat.evnt_recv; + di->acl_recv = rp.r4.stat.acl_recv; + di->acl_sent = rp.r4.stat.acl_sent; + di->sco_recv = rp.r4.stat.sco_recv; + di->sco_sent = rp.r4.stat.sco_sent; + di->bytes_recv = rp.r4.stat.bytes_recv; + di->bytes_sent = rp.r4.stat.bytes_sent; + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK, + &rp.r5, sizeof(rp.r5)) < 0) + goto bad; + di->link_policy_info = rp.r5.policy_mask; + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK, + &rp.r6, sizeof(rp.r6)) < 0) + goto bad; + di->packet_type_info = rp.r6.packet_mask; + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_ROLE_SWITCH, + &rp.r7, sizeof(rp.r7)) < 0) + goto bad; + di->role_switch_info = rp.r7.role_switch; + + if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &rp.r8, sizeof(rp.r8)) < 0) + goto bad; + di->debug = rp.r8.debug; + + rval = 0; +bad: + bt_devclose(s); + + return (rval); +} + +int +bt_devenum(bt_devenum_cb_t cb, void *arg) +{ + struct ng_btsocket_hci_raw_node_list_names rp; + struct bt_devinfo di; + struct sockaddr_hci ha; + int s, i, count; + + rp.num_names = HCI_DEVMAX; + rp.names = (struct nodeinfo *) calloc(rp.num_names, + sizeof(struct nodeinfo)); + if (rp.names == NULL) { + errno = ENOMEM; + return (-1); + } + + memset(&ha, 0, sizeof(ha)); + ha.hci_len = sizeof(ha); + ha.hci_family = AF_BLUETOOTH; + ha.hci_node[0] = 'x'; + + s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI); + if (s < 0) { + free(rp.names); + + return (-1); + } + + if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || + connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || + ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &rp, sizeof(rp)) < 0) { + close(s); + free(rp.names); + + return (-1); + } + + for (count = 0, i = 0; i < rp.num_names; i ++) { + strlcpy(di.devname, rp.names[i].name, sizeof(di.devname)); + if (bt_devinfo(&di) < 0) + continue; + + count ++; + + if (cb == NULL) + continue; + + strlcpy(ha.hci_node, rp.names[i].name, sizeof(ha.hci_node)); + if (bind(s, (struct sockaddr *) &ha, sizeof(ha)) < 0 || + connect(s, (struct sockaddr *) &ha, sizeof(ha)) < 0) + continue; + + if ((*cb)(s, &di, arg) > 0) + break; + } + + close (s); + free(rp.names); + + 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) +{ + static char const * bt_dev_prefix[] = { + "btccc", /* 3Com Bluetooth PC-CARD */ + "h4", /* UART/serial Bluetooth devices */ + "ubt", /* Bluetooth USB devices */ + NULL /* should be last */ + }; + + static char _nodename[HCI_DEVNAME_SIZE]; + char const **p; + char *ep; + int plen, unit; + + if (nodename == NULL) { + nodename = _nodename; + nnlen = HCI_DEVNAME_SIZE; + } + + for (p = bt_dev_prefix; *p != NULL; p ++) { + plen = strlen(*p); + if (strncmp(devname, *p, plen) != 0) + continue; + + unit = strtoul(devname + plen, &ep, 10); + if (*ep != '\0' && + strcmp(ep, "hci") != 0 && + strcmp(ep, "l2cap") != 0) + return (NULL); /* can't make sense of device name */ + + snprintf(nodename, nnlen, "%s%uhci", *p, unit); + + return (nodename); + } + + return (NULL); +} + |