summaryrefslogtreecommitdiffstats
path: root/lib/libbluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libbluetooth')
-rw-r--r--lib/libbluetooth/Makefile53
-rw-r--r--lib/libbluetooth/bluetooth.3726
-rw-r--r--lib/libbluetooth/bluetooth.c371
-rw-r--r--lib/libbluetooth/bluetooth.h212
-rw-r--r--lib/libbluetooth/dev.c95
-rw-r--r--lib/libbluetooth/hci.c734
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);
+}
+
OpenPOWER on IntegriCloud