summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMax Krasnyansky <maxk@qualcomm.com>2002-03-08 21:12:35 +0000
committerMax Krasnyansky <maxk@qualcomm.com>2002-03-08 21:12:35 +0000
commitc98b2f82a4e532ca61592b08e3ad60749eb9f8d7 (patch)
tree19a3df72d2cd6a8d64b7d98473261c7e07e306a0 /tools
Initial revision
Diffstat (limited to 'tools')
-rw-r--r--tools/Makefile.am10
-rw-r--r--tools/Makefile.in433
-rw-r--r--tools/hciattach.8102
-rw-r--r--tools/hciattach.c751
-rw-r--r--tools/hciconfig.c524
-rw-r--r--tools/hcisecfilter.c79
-rw-r--r--tools/hcitool.c368
-rw-r--r--tools/l2ping.849
-rw-r--r--tools/l2ping.c251
-rw-r--r--tools/l2test.c493
-rw-r--r--tools/scotest.c358
11 files changed, 3418 insertions, 0 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 00000000..d3133e5f
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,10 @@
+#
+# $Id$
+#
+
+mandir = $(prefix)/usr/share/man
+
+sbin_PROGRAMS = hciattach hciconfig
+bin_PROGRAMS = hcitool l2ping
+
+man_MANS = hciattach.8 l2ping.8
diff --git a/tools/Makefile.in b/tools/Makefile.in
new file mode 100644
index 00000000..fe838a44
--- /dev/null
+++ b/tools/Makefile.in
@@ -0,0 +1,433 @@
+# Makefile.in generated automatically by automake 1.4-p5 from Makefile.am
+
+# Copyright (C) 1994, 1995-8, 1999, 2001 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+#
+# $Id$
+#
+
+
+SHELL = @SHELL@
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+prefix = @prefix@
+exec_prefix = @exec_prefix@
+
+bindir = @bindir@
+sbindir = @sbindir@
+libexecdir = @libexecdir@
+datadir = @datadir@
+sysconfdir = @sysconfdir@
+sharedstatedir = @sharedstatedir@
+localstatedir = @localstatedir@
+libdir = @libdir@
+infodir = @infodir@
+includedir = @includedir@
+oldincludedir = /usr/include
+
+DESTDIR =
+
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+
+top_builddir = ..
+
+ACLOCAL = @ACLOCAL@
+AUTOCONF = @AUTOCONF@
+AUTOMAKE = @AUTOMAKE@
+AUTOHEADER = @AUTOHEADER@
+
+INSTALL = @INSTALL@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@ $(AM_INSTALL_PROGRAM_FLAGS)
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+transform = @program_transform_name@
+
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_alias = @build_alias@
+build_triplet = @build@
+host_alias = @host_alias@
+host_triplet = @host@
+target_alias = @target_alias@
+target_triplet = @target@
+AR = @AR@
+AWK = @AWK@
+CC = @CC@
+DISTRO = @DISTRO@
+GLIB = @GLIB@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_LDFLAGS = @GLIB_LDFLAGS@
+LD = @LD@
+LEX = @LEX@
+MAKEINFO = @MAKEINFO@
+PACKAGE = @PACKAGE@
+PCMCIA = @PCMCIA@
+VERSION = @VERSION@
+YACC = @YACC@
+
+mandir = $(prefix)/usr/share/man
+
+sbin_PROGRAMS = hciattach hciconfig
+bin_PROGRAMS = hcitool l2ping
+
+man_MANS = hciattach.8 l2ping.8
+mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs
+CONFIG_CLEAN_FILES =
+PROGRAMS = $(bin_PROGRAMS) $(sbin_PROGRAMS)
+
+
+DEFS = @DEFS@ -I. -I$(srcdir)
+CPPFLAGS = @CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBS = @LIBS@
+hcitool_SOURCES = hcitool.c
+hcitool_OBJECTS = hcitool.o
+hcitool_LDADD = $(LDADD)
+hcitool_DEPENDENCIES =
+hcitool_LDFLAGS =
+l2ping_SOURCES = l2ping.c
+l2ping_OBJECTS = l2ping.o
+l2ping_LDADD = $(LDADD)
+l2ping_DEPENDENCIES =
+l2ping_LDFLAGS =
+hciattach_SOURCES = hciattach.c
+hciattach_OBJECTS = hciattach.o
+hciattach_LDADD = $(LDADD)
+hciattach_DEPENDENCIES =
+hciattach_LDFLAGS =
+hciconfig_SOURCES = hciconfig.c
+hciconfig_OBJECTS = hciconfig.o
+hciconfig_LDADD = $(LDADD)
+hciconfig_DEPENDENCIES =
+hciconfig_LDFLAGS =
+CFLAGS = @CFLAGS@
+COMPILE = $(CC) $(DEFS) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@
+man8dir = $(mandir)/man8
+MANS = $(man_MANS)
+
+NROFF = nroff
+DIST_COMMON = Makefile.am Makefile.in
+
+
+DISTFILES = $(DIST_COMMON) $(SOURCES) $(HEADERS) $(TEXINFOS) $(EXTRA_DIST)
+
+TAR = gtar
+GZIP_ENV = --best
+DEP_FILES = .deps/hciattach.P .deps/hciconfig.P .deps/hcitool.P \
+.deps/l2ping.P
+SOURCES = hcitool.c l2ping.c hciattach.c hciconfig.c
+OBJECTS = hcitool.o l2ping.o hciattach.o hciconfig.o
+
+all: all-redirect
+.SUFFIXES:
+.SUFFIXES: .S .c .o .s
+$(srcdir)/Makefile.in: Makefile.am $(top_srcdir)/configure.in $(ACLOCAL_M4)
+ cd $(top_srcdir) && $(AUTOMAKE) --gnu tools/Makefile
+
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status $(BUILT_SOURCES)
+ cd $(top_builddir) \
+ && CONFIG_FILES=$(subdir)/$@ CONFIG_HEADERS= $(SHELL) ./config.status
+
+
+mostlyclean-binPROGRAMS:
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+distclean-binPROGRAMS:
+
+maintainer-clean-binPROGRAMS:
+
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(bindir)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(bin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(bindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+mostlyclean-sbinPROGRAMS:
+
+clean-sbinPROGRAMS:
+ -test -z "$(sbin_PROGRAMS)" || rm -f $(sbin_PROGRAMS)
+
+distclean-sbinPROGRAMS:
+
+maintainer-clean-sbinPROGRAMS:
+
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ $(mkinstalldirs) $(DESTDIR)$(sbindir)
+ @list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ if test -f $$p; then \
+ echo " $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`"; \
+ $(INSTALL_PROGRAM) $$p $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ else :; fi; \
+ done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ list='$(sbin_PROGRAMS)'; for p in $$list; do \
+ rm -f $(DESTDIR)$(sbindir)/`echo $$p|sed 's/$(EXEEXT)$$//'|sed '$(transform)'|sed 's/$$/$(EXEEXT)/'`; \
+ done
+
+.s.o:
+ $(COMPILE) -c $<
+
+.S.o:
+ $(COMPILE) -c $<
+
+mostlyclean-compile:
+ -rm -f *.o core *.core
+
+clean-compile:
+
+distclean-compile:
+ -rm -f *.tab.c
+
+maintainer-clean-compile:
+
+hcitool: $(hcitool_OBJECTS) $(hcitool_DEPENDENCIES)
+ @rm -f hcitool
+ $(LINK) $(hcitool_LDFLAGS) $(hcitool_OBJECTS) $(hcitool_LDADD) $(LIBS)
+
+l2ping: $(l2ping_OBJECTS) $(l2ping_DEPENDENCIES)
+ @rm -f l2ping
+ $(LINK) $(l2ping_LDFLAGS) $(l2ping_OBJECTS) $(l2ping_LDADD) $(LIBS)
+
+hciattach: $(hciattach_OBJECTS) $(hciattach_DEPENDENCIES)
+ @rm -f hciattach
+ $(LINK) $(hciattach_LDFLAGS) $(hciattach_OBJECTS) $(hciattach_LDADD) $(LIBS)
+
+hciconfig: $(hciconfig_OBJECTS) $(hciconfig_DEPENDENCIES)
+ @rm -f hciconfig
+ $(LINK) $(hciconfig_LDFLAGS) $(hciconfig_OBJECTS) $(hciconfig_LDADD) $(LIBS)
+
+install-man8:
+ $(mkinstalldirs) $(DESTDIR)$(man8dir)
+ @list='$(man8_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ if test -f $(srcdir)/$$i; then file=$(srcdir)/$$i; \
+ else file=$$i; fi; \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst"; \
+ $(INSTALL_DATA) $$file $(DESTDIR)$(man8dir)/$$inst; \
+ done
+
+uninstall-man8:
+ @list='$(man8_MANS)'; \
+ l2='$(man_MANS)'; for i in $$l2; do \
+ case "$$i" in \
+ *.8*) list="$$list $$i" ;; \
+ esac; \
+ done; \
+ for i in $$list; do \
+ ext=`echo $$i | sed -e 's/^.*\\.//'`; \
+ inst=`echo $$i | sed -e 's/\\.[0-9a-z]*$$//'`; \
+ inst=`echo $$inst | sed '$(transform)'`.$$ext; \
+ echo " rm -f $(DESTDIR)$(man8dir)/$$inst"; \
+ rm -f $(DESTDIR)$(man8dir)/$$inst; \
+ done
+install-man: $(MANS)
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-man8
+uninstall-man:
+ @$(NORMAL_UNINSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-man8
+
+tags: TAGS
+
+ID: $(HEADERS) $(SOURCES) $(LISP)
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ here=`pwd` && cd $(srcdir) \
+ && mkid -f$$here/ID $$unique $(LISP)
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS)'; \
+ unique=`for i in $$list; do echo $$i; done | \
+ awk ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(ETAGS_ARGS)$$unique$(LISP)$$tags" \
+ || (cd $(srcdir) && etags $(ETAGS_ARGS) $$tags $$unique $(LISP) -o $$here/TAGS)
+
+mostlyclean-tags:
+
+clean-tags:
+
+distclean-tags:
+ -rm -f TAGS ID
+
+maintainer-clean-tags:
+
+distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
+
+subdir = tools
+
+distdir: $(DISTFILES)
+ here=`cd $(top_builddir) && pwd`; \
+ top_distdir=`cd $(top_distdir) && pwd`; \
+ distdir=`cd $(distdir) && pwd`; \
+ cd $(top_srcdir) \
+ && $(AUTOMAKE) --include-deps --build-dir=$$here --srcdir-name=$(top_srcdir) --output-dir=$$top_distdir --gnu tools/Makefile
+ @for file in $(DISTFILES); do \
+ d=$(srcdir); \
+ if test -d $$d/$$file; then \
+ cp -pr $$d/$$file $(distdir)/$$file; \
+ else \
+ test -f $(distdir)/$$file \
+ || ln $$d/$$file $(distdir)/$$file 2> /dev/null \
+ || cp -p $$d/$$file $(distdir)/$$file || :; \
+ fi; \
+ done
+
+DEPS_MAGIC := $(shell mkdir .deps > /dev/null 2>&1 || :)
+
+-include $(DEP_FILES)
+
+mostlyclean-depend:
+
+clean-depend:
+
+distclean-depend:
+ -rm -rf .deps
+
+maintainer-clean-depend:
+
+%.o: %.c
+ @echo '$(COMPILE) -c $<'; \
+ $(COMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ @-cp .deps/$(*F).pp .deps/$(*F).P; \
+ tr ' ' '\012' < .deps/$(*F).pp \
+ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+ >> .deps/$(*F).P; \
+ rm .deps/$(*F).pp
+
+%.lo: %.c
+ @echo '$(LTCOMPILE) -c $<'; \
+ $(LTCOMPILE) -Wp,-MD,.deps/$(*F).pp -c $<
+ @-sed -e 's/^\([^:]*\)\.o[ ]*:/\1.lo \1.o :/' \
+ < .deps/$(*F).pp > .deps/$(*F).P; \
+ tr ' ' '\012' < .deps/$(*F).pp \
+ | sed -e 's/^\\$$//' -e '/^$$/ d' -e '/:$$/ d' -e 's/$$/ :/' \
+ >> .deps/$(*F).P; \
+ rm -f .deps/$(*F).pp
+info-am:
+info: info-am
+dvi-am:
+dvi: dvi-am
+check-am: all-am
+check: check-am
+installcheck-am:
+installcheck: installcheck-am
+install-exec-am: install-binPROGRAMS install-sbinPROGRAMS
+install-exec: install-exec-am
+
+install-data-am: install-man
+install-data: install-data-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+install: install-am
+uninstall-am: uninstall-binPROGRAMS uninstall-sbinPROGRAMS uninstall-man
+uninstall: uninstall-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+all-redirect: all-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) AM_INSTALL_PROGRAM_FLAGS=-s install
+installdirs:
+ $(mkinstalldirs) $(DESTDIR)$(bindir) $(DESTDIR)$(sbindir) \
+ $(DESTDIR)$(mandir)/man8
+
+
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -rm -f Makefile $(CONFIG_CLEAN_FILES)
+ -rm -f config.cache config.log stamp-h stamp-h[0-9]*
+
+maintainer-clean-generic:
+mostlyclean-am: mostlyclean-binPROGRAMS mostlyclean-sbinPROGRAMS \
+ mostlyclean-compile mostlyclean-tags mostlyclean-depend \
+ mostlyclean-generic
+
+mostlyclean: mostlyclean-am
+
+clean-am: clean-binPROGRAMS clean-sbinPROGRAMS clean-compile clean-tags \
+ clean-depend clean-generic mostlyclean-am
+
+clean: clean-am
+
+distclean-am: distclean-binPROGRAMS distclean-sbinPROGRAMS \
+ distclean-compile distclean-tags distclean-depend \
+ distclean-generic clean-am
+
+distclean: distclean-am
+
+maintainer-clean-am: maintainer-clean-binPROGRAMS \
+ maintainer-clean-sbinPROGRAMS maintainer-clean-compile \
+ maintainer-clean-tags maintainer-clean-depend \
+ maintainer-clean-generic distclean-am
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+
+maintainer-clean: maintainer-clean-am
+
+.PHONY: mostlyclean-binPROGRAMS distclean-binPROGRAMS clean-binPROGRAMS \
+maintainer-clean-binPROGRAMS uninstall-binPROGRAMS install-binPROGRAMS \
+mostlyclean-sbinPROGRAMS distclean-sbinPROGRAMS clean-sbinPROGRAMS \
+maintainer-clean-sbinPROGRAMS uninstall-sbinPROGRAMS \
+install-sbinPROGRAMS mostlyclean-compile distclean-compile \
+clean-compile maintainer-clean-compile install-man8 uninstall-man8 \
+install-man uninstall-man tags mostlyclean-tags distclean-tags \
+clean-tags maintainer-clean-tags distdir mostlyclean-depend \
+distclean-depend clean-depend maintainer-clean-depend info-am info \
+dvi-am dvi check check-am installcheck-am installcheck install-exec-am \
+install-exec install-data-am install-data install-am install \
+uninstall-am uninstall all-redirect all-am all installdirs \
+mostlyclean-generic distclean-generic clean-generic \
+maintainer-clean-generic clean mostlyclean distclean maintainer-clean
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/tools/hciattach.8 b/tools/hciattach.8
new file mode 100644
index 00000000..b327336b
--- /dev/null
+++ b/tools/hciattach.8
@@ -0,0 +1,102 @@
+.TH HCIATTACH 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+hciattach \- attach serial devices via UART HCI to BlueZ stack
+.SH SYNOPSIS
+.B hciattach
+<
+.I tty
+> <
+.I type
+|
+.I id
+> [
+.I speed
+] [
+.I flow
+]
+.SH DESCRIPTION
+.LP
+Hciattach is used to attach a serial UART to the Bluetooth stack as HCI
+transport interface.
+.SH OPTIONS
+.TP
+.I <tty>
+This specifies the serial device to attach. A leading
+.B /dev
+can be omitted. Examples:
+.B /dev/ttyS1
+.B ttyS2
+.TP
+.I <type | id>
+The
+.B type
+or
+.B id
+of the Bluetooth device that is to be attached, i.e. vendor or other device
+specific identifier. Currently supported types are
+.RS
+.TP
+.B type
+.B description
+.TP
+any
+Unspecified HCI_UART interface, no vendor specific options
+.TP
+ericsson
+Ericsson based modules
+.TP
+digi
+Digianswer based cards
+.TP
+xircom
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+csr
+CSR Casira serial adapter or BrainBoxes serial dongle (BL642)
+.TP
+bboxes
+BrainBoxes PCMCIA card (BL620)
+.TP
+swave
+Silicon Wave kits
+.RE
+
+Supported IDs are (manufacturer id, product id)
+.RS
+.TP
+0x0105, 0x080a
+Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter
+.TP
+0x0160, 0x0002
+BrainBoxes PCMCIA card (BL620)
+.RE
+
+.TP
+.I <speed>
+The
+.B speed
+specifies the UART speed to use. Baudrates higher than 115.200bps require
+vendor specific initializations that are not implemented for all types of
+devices. In general the following speeds are supported:
+
+.B 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
+
+Supported vendor devices are automatically initialised to their respective
+best settings.
+.TP
+.I <flow>
+If the keyword
+.B flow
+is appended to the list of options then hardware flow control is forced on
+the serial link (
+.B CRTSCTS
+). All above mentioned device types have
+.B flow
+set by default. To force no flow control use
+.B noflow
+instead.
+
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com>
+.PP
+man page by Nils Faerber <nils@kernelconcepts.de>
diff --git a/tools/hciattach.c b/tools/hciattach.c
new file mode 100644
index 00000000..a4489019
--- /dev/null
+++ b/tools/hciattach.c
@@ -0,0 +1,751 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <errno.h>
+#include <time.h>
+#include <termios.h>
+#include <fcntl.h>
+
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_uart.h>
+#include <hci_lib.h>
+
+struct uart_t {
+ char *type;
+ int m_id;
+ int p_id;
+ int proto;
+ int speed;
+ int flags;
+ int (*init) (int fd, struct uart_t *u, struct termios *ti);
+};
+
+#define FLOW_CTL 0x0001
+
+static int uart_speed(int s)
+{
+ switch (s) {
+ case 9600:
+ return B9600;
+ case 19200:
+ return B19200;
+ case 38400:
+ return B38400;
+ case 57600:
+ return B57600;
+ case 115200:
+ return B115200;
+ case 230400:
+ return B230400;
+ case 460800:
+ return B460800;
+ case 921600:
+ return B921600;
+ default:
+ return B57600;
+ }
+}
+
+static int set_speed(int fd, struct termios *ti, int speed)
+{
+ cfsetospeed(ti, uart_speed(speed));
+ return tcsetattr(fd, TCSANOW, ti);
+}
+
+static void sig_alarm(int sig)
+{
+ fprintf(stderr, "Initialization timed out.\n");
+ exit(1);
+}
+
+/*
+ * Read an HCI event from the given file descriptor.
+ */
+static int read_hci_event(int fd, unsigned char* buf, int size)
+{
+ int remain, r;
+ int count = 0;
+
+ if (size <= 0)
+ return -1;
+
+ /* The first byte identifies the packet type. For HCI event packets, it
+ * should be 0x04, so we read until we get to the 0x04. */
+ while (1) {
+ r = read(fd, buf, 1);
+ if (r <= 0)
+ return -1;
+ if (buf[0] == 0x04)
+ break;
+ }
+ count++;
+
+ /* The next two bytes are the event code and parameter total length. */
+ while (count < 3) {
+ r = read(fd, buf + count, 3 - count);
+ if (r <= 0)
+ return -1;
+ count += r;
+ }
+
+ /* Now we read the parameters. */
+ if (buf[2] < (size - 3))
+ remain = buf[2];
+ else
+ remain = size - 3;
+
+ while ((count - 3) < remain) {
+ r = read(fd, buf + count, remain - (count - 3));
+ if (r <= 0)
+ return -1;
+ count += r;
+ }
+ return count;
+}
+
+/*
+ * Ericsson specific initialization
+ */
+static int ericsson(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[10];
+
+ /* Switch to default Ericsson baudrate*/
+ if (set_speed(fd, ti, 57600) < 0) {
+ perror("Can't set default baud rate");
+ return -1;
+ }
+
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x09;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x01;
+
+ switch (u->speed) {
+ case 57600:
+ cmd[4] = 0x03;
+ break;
+ case 115200:
+ cmd[4] = 0x02;
+ break;
+ case 230400:
+ cmd[4] = 0x01;
+ break;
+ case 460800:
+ cmd[4] = 0x00;
+ break;
+ default:
+ cmd[4] = 0x03;
+ u->speed = 57600;
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 5) != 5) {
+ perror("Failed to write init command");
+ return -1;
+ }
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+/*
+ * Digianswer specific initialization
+ */
+static int digi(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 50000};
+ char cmd[10];
+
+ /* Switch to default Digi baudrate*/
+ if (set_speed(fd, ti, 9600) < 0) {
+ perror("Can't set default baud rate");
+ return -1;
+ }
+
+ /* DigiAnswer set baud rate command */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x07;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x01;
+
+ switch (u->speed) {
+ case 57600:
+ cmd[4] = 0x08;
+ break;
+ case 115200:
+ cmd[4] = 0x09;
+ break;
+ default:
+ cmd[4] = 0x09;
+ u->speed = 115200;
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 5) != 5) {
+ perror("Failed to write init command");
+ return -1;
+ }
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+/*
+ * CSR specific initialization
+ * Inspired strongly by code in OpenBT and experimentations with Brainboxes
+ * Pcmcia card.
+ * Jean Tourrilhes <jt@hpl.hp.com> - 14.11.01
+ */
+static int csr(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 10000000}; /* 10ms - be generous */
+ unsigned char cmd[30]; /* Command */
+ unsigned char resp[30]; /* Response */
+ int clen = 0; /* Command len */
+ static int csr_seq = 0; /* Sequence number of command */
+ int divisor;
+
+ /* Switch to default CSR baudrate */
+ if (set_speed(fd, ti, 115200) < 0) {
+ perror("Can't set default baud rate");
+ return -1;
+ }
+
+ /* It seems that if we set the CSR UART speed straight away, it
+ * won't work, the CSR UART gets into a state where we can't talk
+ * to it anymore.
+ * On the other hand, doing a read before setting the CSR speed
+ * seems to be ok.
+ * Therefore, the strategy is to read the build ID (useful for
+ * debugging) and only then set the CSR UART speed. Doing like
+ * this is more complex but at least it works ;-)
+ * The CSR UART control may be slow to wake up or something because
+ * every time I read its speed, its bogus...
+ * Jean II */
+
+ /* Try to read the build ID of the CSR chip */
+ clen = 5 + (5 + 6) * 2;
+ /* HCI header */
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x00; /* CSR command */
+ cmd[2] = 0xfc; /* MANUFACTURER_SPEC */
+ cmd[3] = 1 + (5 + 6) * 2; /* len */
+ /* CSR MSG header */
+ cmd[4] = 0xC2; /* first+last+channel=BCC */
+ /* CSR BCC header */
+ cmd[5] = 0x00; /* type = GET-REQ */
+ cmd[6] = 0x00; /* - msB */
+ cmd[7] = 5 + 4; /* len */
+ cmd[8] = 0x00; /* - msB */
+ cmd[9] = csr_seq & 0xFF;/* seq num */
+ cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */
+ csr_seq++;
+ cmd[11] = 0x19; /* var_id = CSR_CMD_BUILD_ID */
+ cmd[12] = 0x28; /* - msB */
+ cmd[13] = 0x00; /* status = STATUS_OK */
+ cmd[14] = 0x00; /* - msB */
+ /* CSR BCC payload */
+ memset(cmd + 15, 0, 6 * 2);
+
+ /* Send command */
+ do {
+ if (write(fd, cmd, clen) != clen) {
+ perror("Failed to write init command (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Event code 0xFF is for vendor-specific events, which is
+ * what we're looking for. */
+ } while (resp[1] != 0xFF);
+
+#ifdef CSR_DEBUG
+ {
+ char temp[512];
+ int i;
+ for (i=0; i < rlen; i++)
+ sprintf(temp + (i*3), "-%02X", resp[i]);
+ fprintf(stderr, "Reading CSR build ID %d [%s]\n", rlen, temp + 1);
+ // In theory, it should look like :
+ // 04-FF-13-FF-01-00-09-00-00-00-19-28-00-00-73-00-00-00-00-00-00-00
+ }
+#endif
+ /* Display that to user */
+ fprintf(stderr, "CSR build ID 0x%02X-0x%02X\n",
+ resp[15] & 0xFF, resp[14] & 0xFF);
+
+ /* Try to read the current speed of the CSR chip */
+ clen = 5 + (5 + 4)*2;
+ /* -- HCI header */
+ cmd[3] = 1 + (5 + 4)*2; /* len */
+ /* -- CSR BCC header -- */
+ cmd[9] = csr_seq & 0xFF; /* seq num */
+ cmd[10] = (csr_seq >> 8) & 0xFF; /* - msB */
+ csr_seq++;
+ cmd[11] = 0x02; /* var_id = CONFIG_UART */
+ cmd[12] = 0x68; /* - msB */
+
+#ifdef CSR_DEBUG
+ /* Send command */
+ do {
+ if (write(fd, cmd, clen) != clen) {
+ perror("Failed to write init command (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Read reply. */
+ if (read_hci_event(fd, resp, 100) < 0) {
+ perror("Failed to read init response (GET_BUILD_ID)");
+ return -1;
+ }
+
+ /* Event code 0xFF is for vendor-specific events, which is
+ * what we're looking for. */
+ } while (resp[1] != 0xFF);
+
+ {
+ char temp[512];
+ int i;
+ for (i=0; i < rlen; i++)
+ sprintf(temp + (i*3), "-%02X", resp[i]);
+ fprintf(stderr, "Reading CSR UART speed %d [%s]\n", rlen, temp+1);
+ }
+#endif
+
+ /* Now, create the command that will set the UART speed */
+ /* CSR BCC header */
+ cmd[5] = 0x02; /* type = SET-REQ */
+ cmd[6] = 0x00; /* - msB */
+ cmd[9] = csr_seq & 0xFF; /* seq num */
+ cmd[10] = (csr_seq >> 8) & 0xFF;/* - msB */
+ csr_seq++;
+
+ switch (u->speed) {
+ case 9600:
+ divisor = 0x0027;
+ break;
+ /* Various speeds ommited */
+ case 57600:
+ divisor = 0x00EC;
+ break;
+ case 115200:
+ divisor = 0x01D8;
+ break;
+ /* For Brainbox Pcmcia cards */
+ case 460800:
+ divisor = 0x075F;
+ break;
+ case 921600:
+ divisor = 0x0EBF;
+ break;
+ default:
+ /* Safe default */
+ divisor = 0x01D8;
+ u->speed = 115200;
+ break;
+ }
+ /* No parity, one stop bit -> divisor |= 0x0000; */
+ cmd[15] = (divisor) & 0xFF; /* divider */
+ cmd[16] = (divisor >> 8) & 0xFF; /* - msB */
+ /* The rest of the payload will be 0x00 */
+
+#ifdef CSR_DEBUG
+ {
+ char temp[512];
+ int i;
+ for(i = 0; i < clen; i++)
+ sprintf(temp + (i*3), "-%02X", cmd[i]);
+ fprintf(stderr, "Writing CSR UART speed %d [%s]\n", clen, temp + 1);
+ // In theory, it should look like :
+ // 01-00-FC-13-C2-02-00-09-00-03-00-02-68-00-00-BF-0E-00-00-00-00-00-00
+ // 01-00-FC-13-C2-02-00-09-00-01-00-02-68-00-00-D8-01-00-00-00-00-00-00
+ }
+#endif
+
+ /* Send the command to set the CSR UART speed */
+ if (write(fd, cmd, clen) != clen) {
+ perror("Failed to write init command (SET_UART_SPEED)");
+ return -1;
+ }
+ nanosleep(&tm, NULL);
+ return 0;
+}
+
+/*
+ * Silicon Wave specific initialization
+ * Thomas Moser <Thomas.Moser@tmoser.ch>
+ */
+static int swave(int fd, struct uart_t *u, struct termios *ti)
+{
+ struct timespec tm = {0, 500000};
+ char cmd[10], rsp[100];
+ int r;
+
+ /* Switch to default Silicon Wave baudrate*/
+ if (set_speed(fd, ti, 115200) < 0) {
+ perror("Can't set default baud rate");
+ return -1;
+ }
+
+ // Silicon Wave set baud rate command
+ // see HCI Vendor Specific Interface from Silicon Wave
+ // first send a "param access set" command to set the
+ // appropriate data fields in RAM. Then send a "HCI Reset
+ // Subcommand", e.g. "soft reset" to make the changes effective.
+
+ cmd[0] = HCI_COMMAND_PKT; // it's a command packet
+ cmd[1] = 0x0B; // OCF 0x0B = param access set
+ cmd[2] = 0xfc; // OGF bx111111 = vendor specific
+ cmd[3] = 0x06; // 6 bytes of data following
+ cmd[4] = 0x01; // param sub command
+ cmd[5] = 0x11; // tag 17 = 0x11 = HCI Transport Params
+ cmd[6] = 0x03; // length of the parameter following
+ cmd[7] = 0x01; // HCI Transport flow control enable
+ cmd[8] = 0x01; // HCI Transport Type = UART
+
+ switch (u->speed) {
+ case 19200:
+ cmd[9] = 0x03;
+ break;
+ case 38400:
+ cmd[9] = 0x02;
+ break;
+ case 57600:
+ cmd[9] = 0x01;
+ break;
+ case 115200:
+ cmd[9] = 0x00;
+ break;
+ default:
+ u->speed = 115200;
+ cmd[9] = 0x00;
+ break;
+ }
+
+ /* Send initialization command */
+ if (write(fd, cmd, 10) != 5) {
+ perror("Failed to write init command");
+ return -1;
+ }
+
+ // We should wait for a "GET Event" to confirm the success of
+ // the baud rate setting. Wait some time before reading. Better:
+ // read with timeout, parse data
+ // until correct answer, else error handling ... todo ...
+
+ nanosleep(&tm, NULL);
+
+ r = read(fd, rsp, sizeof(rsp));
+ if (r > 0) {
+ // guess it's okay, but we should parse the reply. But since
+ // I don't react on an error anyway ... todo
+ // Response packet format:
+ // 04 Event
+ // FF Vendor specific
+ // 07 Parameter length
+ // 0B Subcommand
+ // 01 Setevent
+ // 11 Tag specifying HCI Transport Layer Parameter
+ // 03 length
+ // 01 flow on
+ // 01 Hci Transport type = Uart
+ // xx Baud rate set (see above)
+ } else {
+ // ups, got error.
+ return -1;
+ }
+
+ // we probably got the reply. Now we must send the "soft reset":
+ cmd[0] = HCI_COMMAND_PKT; // it's a command packet
+ cmd[1] = 0x0B; // OCF 0x0B = param access set
+ cmd[2] = 0xfc; // OGF bx111111 = vendor specific
+ cmd[3] = 0x01; // 1 byte of data following
+ cmd[4] = 0x03; // HCI Reset Subcommand
+
+ // Send initialization command
+ if (write(fd, cmd, 5) != 5) {
+ perror("Can't write Silicon Wave reset cmd.");
+ return -1;
+ }
+
+ nanosleep(&tm, NULL);
+
+ // now the uart baud rate on the silicon wave module is set and effective.
+ // change our own baud rate as well. Then there is a reset event comming in
+ // on the *new* baud rate. This is *undocumented*! The packet looks like this:
+ // 04 FF 01 0B (which would make that a confirmation of 0x0B = "Param
+ // subcommand class". So: change to new baud rate, read with timeout, parse
+ // data, error handling. BTW: all param access in Silicon Wave is done this way.
+ // Maybe this code would belong in a seperate file, or at least code reuse...
+
+ return 0;
+}
+
+struct uart_t uart[] = {
+ { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, FLOW_CTL, NULL },
+ { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 115200, FLOW_CTL, ericsson },
+ { "digi", 0x0000, 0x0000, HCI_UART_H4, 115200, FLOW_CTL, digi },
+
+ /* Xircom PCMCIA cards: Credit Card Adapter and Real Port Adapter */
+ { "xircom", 0x0105, 0x080a, HCI_UART_H4, 115200, FLOW_CTL, NULL },
+
+ /* CSR Casira serial adapter or BrainBoxes serial dongle (BL642) */
+ { "csr", 0x0000, 0x0000, HCI_UART_H4, 115200, FLOW_CTL, csr },
+
+ /* BrainBoxes PCMCIA card (BL620) */
+ { "bboxes", 0x0160, 0x0002, HCI_UART_H4, 460800, FLOW_CTL, csr },
+
+ /* Silicon Wave kits */
+ { "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, FLOW_CTL, swave },
+
+ /* Sphinx Electronics PICO Card */
+ { "picocard", 0x025e, 0x1000, HCI_UART_H4, 115200, FLOW_CTL, NULL },
+
+ { NULL, 0 }
+};
+
+struct uart_t * get_by_id(int m_id, int p_id)
+{
+ int i;
+ for (i = 0; uart[i].type; i++) {
+ if (uart[i].m_id == m_id && uart[i].p_id == p_id)
+ return &uart[i];
+ }
+ return NULL;
+}
+
+struct uart_t * get_by_type(char *type)
+{
+ int i;
+ for (i = 0; uart[i].type; i++) {
+ if (!strcmp(uart[i].type, type))
+ return &uart[i];
+ }
+ return NULL;
+}
+
+/* Initialize UART driver */
+int init_uart(char *dev, struct uart_t *u)
+{
+ struct termios ti;
+ int fd, i;
+
+ fd = open(dev, O_RDWR | O_NOCTTY);
+ if (fd < 0) {
+ perror("Can't open serial port");
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &ti) < 0) {
+ perror("Can't get port settings");
+ return -1;
+ }
+
+ cfmakeraw(&ti);
+
+ ti.c_cflag |= CLOCAL;
+ if (u->flags & FLOW_CTL)
+ ti.c_cflag |= CRTSCTS;
+ else
+ ti.c_cflag &= ~CRTSCTS;
+
+ if (tcsetattr(fd, TCSANOW, &ti) < 0) {
+ perror("Can't set port settings");
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (u->init && u->init(fd, u, &ti) < 0)
+ return -1;
+
+ tcflush(fd, TCIOFLUSH);
+
+ /* Set actual baudrate */
+ if (set_speed(fd, &ti, u->speed) < 0) {
+ perror("Can't set baud rate");
+ return -1;
+ }
+
+ /* Set TTY to N_HCI line discpline */
+ i = N_HCI;
+ if (ioctl(fd, TIOCSETD, &i) < 0) {
+ perror("Can't set line disc");
+ return -1;
+ }
+
+ if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+ perror("Can't set device");
+ return -1;
+ }
+
+ return fd;
+}
+
+static void usage(void)
+{
+ printf("hciattach - HCI UART driver initialization utility\n");
+ printf("Usage:\n");
+ printf("\thciattach <tty> <type | id> [speed] [flow]\n");
+ printf("\thciattach -l\n");
+}
+
+extern int optind, opterr, optopt;
+extern char *optarg;
+
+int main(int argc, char *argv[])
+{
+ struct uart_t *u = NULL;
+ int detach, opt, i, n;
+ int to = 5;
+ struct sigaction sa;
+ char dev[20];
+
+ detach = 1;
+
+ while ((opt=getopt(argc, argv, "nt:l")) != EOF) {
+ switch(opt) {
+ case 'n':
+ detach = 0;
+ break;
+
+ case 't':
+ to = atoi(optarg);
+ break;
+
+ case 'l':
+ for (i = 0; uart[i].type; i++) {
+ printf("%-10s0x%04x,0x%04x\n", uart[i].type,
+ uart[i].m_id, uart[i].p_id);
+ }
+ exit(0);
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ n = argc - optind;
+ if (n < 2) {
+ usage();
+ exit(1);
+ }
+
+ for (n = 0; optind < argc; n++, optind++) {
+ char *opt;
+
+ opt = argv[optind];
+
+ switch(n) {
+ case 0:
+ dev[0] = 0;
+ if (!strchr(opt, '/'))
+ strcpy(dev, "/dev/");
+ strcat(dev, opt);
+ break;
+
+ case 1:
+ if (strchr(argv[optind], ',')) {
+ int m_id, p_id;
+ sscanf(argv[optind], "%x,%x", &m_id, &p_id);
+ u = get_by_id(m_id, p_id);
+ } else {
+ u = get_by_type(opt);
+ }
+
+ if (!u) {
+ fprintf(stderr, "Unknow device type or id\n");
+ exit(1);
+ }
+
+ break;
+
+ case 2:
+ u->speed = atoi(argv[optind]);
+ break;
+
+ case 3:
+ if (!strcmp("flow", argv[optind]))
+ u->flags |= FLOW_CTL;
+ else
+ u->flags &= ~FLOW_CTL;
+ break;
+ }
+ }
+
+ if (!u) {
+ fprintf(stderr, "Unknow device type or id\n");
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_alarm;
+ sigaction(SIGALRM, &sa, NULL);
+
+ /* 5 seconds should be enought for intialization */
+ alarm(to);
+
+ n = init_uart(dev, u);
+ if (n < 0) {
+ perror("Can't init device");
+ exit(1);
+ }
+
+ alarm(0);
+
+ if (detach) {
+ if (fork()) return 0;
+ for (i=0; i<20; i++)
+ if (i != n) close(i);
+ }
+
+ while (1) sleep(999999999);
+ return 0;
+}
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
new file mode 100644
index 00000000..3cbec922
--- /dev/null
+++ b/tools/hciconfig.c
@@ -0,0 +1,524 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_lib.h>
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+static struct hci_dev_info di;
+static int all;
+
+void print_dev_hdr(struct hci_dev_info *di);
+void print_dev_info(int ctl, struct hci_dev_info *di);
+
+void print_dev_list(int ctl, int flags)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i;
+
+ if( !(dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t))) ) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if( ioctl(ctl, HCIGETDEVLIST, (void*)dl) ) {
+ perror("Can't get device list");
+ exit(1);
+ }
+ for(i=0; i< dl->dev_num; i++) {
+ di.dev_id = (dr+i)->dev_id;
+ if( ioctl(ctl, HCIGETDEVINFO, (void*)&di) )
+ continue;
+ print_dev_info(ctl, &di);
+ }
+}
+
+void print_pkt_type(struct hci_dev_info *di)
+{
+ printf("\tPacket type: %s\n", hci_ptypetostr(di->pkt_type));
+}
+
+void print_link_policy(struct hci_dev_info *di)
+{
+ printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
+}
+
+void print_link_mode(struct hci_dev_info *di)
+{
+ printf("\tLink mode: %s\n", hci_lmtostr(di->link_mode));
+}
+
+void print_dev_features(struct hci_dev_info *di)
+{
+ printf("\tFeatures: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
+ di->features[0], di->features[1],
+ di->features[2], di->features[3] );
+}
+
+void cmd_rstat(int ctl, int hdev, char *opt)
+{
+ /* Reset HCI device stat counters */
+ if( ioctl(ctl, HCIDEVRESTAT, hdev) < 0 ) {
+ printf("Can't reset stats counters hci%d. %s(%d)\n", hdev,
+ strerror(errno), errno);
+ exit(1);
+ }
+}
+
+void cmd_scan(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+ dr.dev_opt = SCAN_DISABLED;
+ if( !strcmp(opt, "iscan") )
+ dr.dev_opt = SCAN_INQUIRY;
+ else if( !strcmp(opt, "pscan") )
+ dr.dev_opt = SCAN_PAGE;
+ else if( !strcmp(opt, "piscan") )
+ dr.dev_opt = SCAN_PAGE | SCAN_INQUIRY;
+
+ if( ioctl(ctl, HCISETSCAN, (unsigned long)&dr) < 0 ) {
+ printf("Can't set scan mode on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+void cmd_auth(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+ if( !strcmp(opt, "auth") )
+ dr.dev_opt = AUTH_ENABLED;
+ else
+ dr.dev_opt = AUTH_DISABLED;
+
+ if( ioctl(ctl, HCISETAUTH, (unsigned long)&dr) < 0 ) {
+ printf("Can't set auth on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+void cmd_encrypt(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+ if( !strcmp(opt, "encrypt") )
+ dr.dev_opt = ENCRYPT_P2P;
+ else
+ dr.dev_opt = ENCRYPT_DISABLED;
+
+ if( ioctl(ctl, HCISETENCRYPT, (unsigned long)&dr) < 0 ) {
+ printf("Can't set encrypt on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+void cmd_up(int ctl, int hdev, char *opt)
+{
+ int ret;
+
+ /* Start HCI device */
+ if( (ret = ioctl(ctl, HCIDEVUP, hdev)) < 0 ) {
+ if( errno == EALREADY )
+ return;
+ printf("Can't init device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+ cmd_scan(ctl, hdev, "piscan");
+}
+
+void cmd_down(int ctl, int hdev, char *opt)
+{
+ /* Stop HCI device */
+ if (ioctl(ctl, HCIDEVDOWN, hdev) < 0) {
+ printf("Can't down device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+}
+
+void cmd_reset(int ctl, int hdev, char *opt)
+{
+ /* Reset HCI device
+ if( ioctl(ctl, HCIDEVRESET, hdev) < 0 ){
+ printf("Reset failed hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+ */
+ cmd_down(ctl, hdev, "down");
+ cmd_up(ctl, hdev, "up");
+}
+
+void cmd_ptype(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+
+ if (hci_strtoptype(opt, &dr.dev_opt)) {
+ if (ioctl(ctl, HCISETPTYPE, (unsigned long)&dr) < 0) {
+ printf("Can't set pkttype on hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ print_dev_hdr(&di);
+ print_pkt_type(&di);
+ }
+}
+
+void cmd_lp(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+
+ if (hci_strtolp(opt, &dr.dev_opt)) {
+ if (ioctl(ctl, HCISETLINKPOL, (unsigned long)&dr) < 0) {
+ printf("Can't set link policy on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ print_dev_hdr(&di);
+ print_link_policy(&di);
+ }
+}
+
+void cmd_lm(int ctl, int hdev, char *opt)
+{
+ struct hci_dev_req dr;
+
+ dr.dev_id = hdev;
+
+ if (hci_strtolm(opt, &dr.dev_opt)) {
+ if (ioctl(ctl, HCISETLINKMODE, (unsigned long)&dr) < 0) {
+ printf("Can't set default link mode on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ print_dev_hdr(&di);
+ print_link_mode(&di);
+ }
+}
+
+void cmd_features(int ctl, int hdev, char *opt)
+{
+ print_dev_hdr(&di);
+ print_dev_features(&di);
+}
+
+void cmd_name(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ int s;
+ if ((s = hci_open_dev(hdev)) < 0) {
+ printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rq, 0, sizeof(rq));
+
+ if (opt) {
+ change_local_name_cp cp;
+ strcpy(cp.name, opt);
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_CHANGE_LOCAL_NAME;
+ rq.cparam = &cp;
+ rq.clen = CHANGE_LOCAL_NAME_CP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ printf("Can't change local name on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ read_local_name_rp rp;
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_LOCAL_NAME;
+ rq.rparam = &rp;
+ rq.rlen = READ_LOCAL_NAME_RP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ printf("Can't read local name on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ if (rp.status) {
+ printf("Read local name on hci%d returned status %d\n", hdev, rp.status);
+ exit(1);
+ }
+ print_dev_hdr(&di);
+ printf("\tName: '%s'\n", rp.name);
+ }
+}
+
+void cmd_class(int ctl, int hdev, char *opt)
+{
+ struct hci_request rq;
+ int s;
+
+ if ((s = hci_open_dev(hdev)) < 0) {
+ printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rq, 0, sizeof(rq));
+ if (opt) {
+ uint32_t cod = htobl(strtoul(opt, NULL, 16));
+ write_class_of_dev_cp cp;
+
+ memcpy(cp.dev_class, &cod, 3);
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_WRITE_CLASS_OF_DEV;
+ rq.cparam = &cp;
+ rq.clen = WRITE_CLASS_OF_DEV_CP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ printf("Can't write local class of device on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+ } else {
+ read_class_of_dev_rp rp;
+
+ rq.ogf = OGF_HOST_CTL;
+ rq.ocf = OCF_READ_CLASS_OF_DEV;
+ rq.rparam = &rp;
+ rq.rlen = READ_CLASS_OF_DEV_RP_SIZE;
+
+ if (hci_send_req(s, &rq, 1000) < 0) {
+ printf("Can't read class of device on hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (rp.status) {
+ printf("Read class of device on hci%d returned status %d\n",
+ hdev, rp.status);
+ exit(1);
+ }
+ print_dev_hdr(&di);
+ printf("\tClass: 0x%02x%02x%02x\n",
+ rp.dev_class[2], rp.dev_class[1], rp.dev_class[0]);
+ }
+}
+
+void cmd_version(int ctl, int hdev, char *opt)
+{
+ struct hci_version ver;
+ int dd;
+
+ dd = hci_open_dev(hdev);
+ if (dd < 0) {
+ printf("Can't open device hci%d. %s(%d)\n", hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_read_local_version(dd, &ver, 1000) < 0) {
+ printf("Can't read version info hci%d. %s(%d)\n",
+ hdev, strerror(errno), errno);
+ exit(1);
+ }
+
+ print_dev_hdr(&di);
+ printf( "\tHCI Ver: 0x%x HCI Rev: 0x%x LMP Ver: 0x%x LMP Subver: 0x%x\n"
+ "\tManufacturer: %d\n",
+ ver.hci_ver, ver.hci_rev, ver.lmp_ver, ver.lmp_subver,
+ ver.manufacturer);
+}
+
+void print_dev_hdr(struct hci_dev_info *di)
+{
+ static int hdr = -1;
+ bdaddr_t bdaddr;
+
+ if (hdr == di->dev_id)
+ return;
+ hdr = di->dev_id;
+
+ baswap(&bdaddr, &di->bdaddr);
+
+ printf("%s:\tType: %s\n", di->name, hci_dtypetostr(di->type) );
+ printf("\tBD Address: %s ACL MTU: %d:%d SCO: MTU %d:%d\n",
+ batostr(&bdaddr), di->acl_mtu, di->acl_max,
+ di->sco_mtu, di->sco_max);
+}
+
+void print_dev_info(int ctl, struct hci_dev_info *di)
+{
+ struct hci_dev_stats *st = &di->stat;
+
+ print_dev_hdr(di);
+
+ printf("\t%s\n", hci_dflagstostr(di->flags) );
+
+ printf("\tRX bytes:%d acl:%d sco:%d events:%d errors:%d\n",
+ st->byte_rx, st->acl_rx, st->sco_rx, st->evt_rx, st->err_rx);
+
+ printf("\tTX bytes:%d acl:%d sco:%d commands:%d errors:%d\n",
+ st->byte_tx, st->acl_tx, st->sco_tx, st->cmd_tx, st->err_tx);
+
+ if (all) {
+ print_dev_features(di);
+ print_pkt_type(di);
+ print_link_policy(di);
+ print_link_mode(di);
+
+ cmd_name(ctl, di->dev_id, NULL);
+ cmd_class(ctl, di->dev_id, NULL);
+ cmd_version(ctl, di->dev_id, NULL);
+ }
+
+ printf("\n");
+}
+
+struct {
+ char *cmd;
+ void (*func)(int ctl, int hdev, char *opt);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "up", cmd_up, 0, "Open and initialize HCI device" },
+ { "down", cmd_down, 0, "Close HCI device" },
+ { "reset", cmd_reset, 0, "Reset HCI device" },
+ { "rstat", cmd_rstat, 0, "Reset statistic counters" },
+ { "auth", cmd_auth, 0, "Enable Authentication" },
+ { "noauth", cmd_auth, 0, "Disable Authentication" },
+ { "encrypt",cmd_encrypt,0, "Enable Encryption" },
+ { "noencrypt", cmd_encrypt, 0, "Disable Encryption" },
+ { "piscan", cmd_scan, 0, "Enable Page and Inquiry scan" },
+ { "noscan", cmd_scan, 0, "Disable scan" },
+ { "iscan", cmd_scan, 0, "Enable Inquiry scan" },
+ { "pscan", cmd_scan, 0, "Enable Page scan" },
+ { "ptype", cmd_ptype, "[type]", "Get/Set default packet type" },
+ { "lm", cmd_lm, "[mode]", "Get/Set default link mode" },
+ { "lp", cmd_lp, "[policy]", "Get/Set default link policy" },
+ { "name", cmd_name, "[name]", "Get/Set local name" },
+ { "class", cmd_class, "[class]", "Get/Set class of device" },
+ { "version", cmd_version, 0, "Display version information" },
+ { "features", cmd_features, 0,"Display device features" },
+ { NULL, NULL, 0}
+};
+
+void usage(void)
+{
+ int i;
+
+ printf("hciconfig - HCI device configuration utility\n");
+ printf("Usage:\n"
+ "\thciconfig\n"
+ "\thciconfig [-a] hciX [command]\n");
+ printf("Commands:\n");
+ for (i=0; command[i].cmd; i++)
+ printf("\t%-10s %-8s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+}
+
+int main(int argc, char *argv[], char *env[])
+{
+ int opt, ctl, i, cmd=0;
+ char *dev;
+
+ while ((opt=getopt(argc, argv,"ha")) != EOF) {
+ switch(opt) {
+ case 'a':
+ all = 1;
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ }
+ }
+
+ /* Open HCI socket */
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+ perror("Can't open HCI socket.");
+ exit(1);
+ }
+
+ if (argc - optind < 1) {
+ print_dev_list(ctl, 0);
+ exit(0);
+ }
+
+ dev = strdup(argv[optind]);
+ di.dev_id = atoi(argv[optind]+3);
+ optind++;
+
+ if (ioctl(ctl, HCIGETDEVINFO, (void*)&di)) {
+ perror("Can't get device info");
+ exit(1);
+ }
+
+ while (optind < argc) {
+ for (i=0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[optind],4))
+ continue;
+
+ if (command[i].opt)
+ optind++;
+
+ command[i].func(ctl, di.dev_id, argv[optind]);
+ cmd = 1;
+ break;
+ }
+ optind++;
+ }
+
+ if (!cmd)
+ print_dev_info(ctl, &di);
+
+ close(ctl);
+ return 0;
+}
diff --git a/tools/hcisecfilter.c b/tools/hcisecfilter.c
new file mode 100644
index 00000000..bee16df1
--- /dev/null
+++ b/tools/hcisecfilter.c
@@ -0,0 +1,79 @@
+#include <stdio.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+int main(void)
+{
+ uint32_t type_mask;
+ uint32_t event_mask[2];
+ uint32_t ocf_mask[4];
+
+ // Packet types
+ memset((void *)&type_mask, 0, sizeof(type_mask));
+ hci_set_bit(HCI_EVENT_PKT, &type_mask);
+
+ printf("Type mask: { 0x%lx }\n", type_mask);
+
+ // Events
+ memset((void *)event_mask, 0, sizeof(event_mask));
+ hci_set_bit(EVT_INQUIRY_COMPLETE, event_mask);
+ hci_set_bit(EVT_INQUIRY_RESULT, event_mask);
+
+ hci_set_bit(EVT_CONN_COMPLETE, event_mask);
+ hci_set_bit(EVT_CONN_REQUEST, event_mask);
+ hci_set_bit(EVT_DISCONN_COMPLETE, event_mask);
+
+ hci_set_bit(EVT_AUTH_COMPLETE, event_mask);
+ hci_set_bit(EVT_ENCRYPT_CHANGE, event_mask);
+
+ hci_set_bit(EVT_CMD_COMPLETE, event_mask);
+ hci_set_bit(EVT_CMD_STATUS, event_mask);
+
+ hci_set_bit(EVT_READ_REMOTE_FEATURES_COMPLETE, event_mask);
+ hci_set_bit(EVT_READ_REMOTE_VERSION_COMPLETE, event_mask);
+ hci_set_bit(EVT_REMOTE_NAME_REQ_COMPLETE, event_mask);
+
+ printf("Event mask: { 0x%lx, 0x%lx }\n", event_mask[0], event_mask[1]);
+
+ // OGF_LINK_CTL
+ memset((void *) ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_INQUIRY, ocf_mask);
+ hci_set_bit(OCF_REMOTE_NAME_REQ, ocf_mask);
+ hci_set_bit(OCF_READ_REMOTE_FEATURES, ocf_mask);
+ hci_set_bit(OCF_READ_REMOTE_VERSION, ocf_mask);
+
+ printf("OGF_LINK_CTL: { 0x%lx, 0x%lx, 0x%lx, 0x%lx }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ // OGF_LINK_POLICY
+ memset((void *) ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_ROLE_DISCOVERY, ocf_mask);
+ hci_set_bit(OCF_READ_LINK_POLICY, ocf_mask);
+
+ printf("OGF_LINK_POLICY: { 0x%lx, 0x%lx, 0x%lx, 0x%lx }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ // OGF_HOST_CTL
+ memset((void *) ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_READ_AUTH_ENABLE, ocf_mask);
+ hci_set_bit(OCF_READ_ENCRYPT_MODE, ocf_mask);
+ hci_set_bit(OCF_READ_LOCAL_NAME, ocf_mask);
+ hci_set_bit(OCF_READ_CLASS_OF_DEV, ocf_mask);
+
+ printf("OGF_HOST_CTL: { 0x%lx, 0x%lx, 0x%lx, 0x%lx }\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+
+ // OGF_INFO_PARAM
+ memset((void *) ocf_mask, 0, sizeof(ocf_mask));
+ hci_set_bit(OCF_READ_LOCAL_VERSION, ocf_mask);
+ hci_set_bit(OCF_READ_LOCAL_FEATURES, ocf_mask);
+ hci_set_bit(OCF_READ_BUFFER_SIZE, ocf_mask);
+ hci_set_bit(OCF_READ_BD_ADDR, ocf_mask);
+ hci_set_bit(OCF_READ_BD_ADDR, ocf_mask);
+
+ printf("OGF_INFO_PARAM: { 0x%lx, 0x%lx, 0x%lx, 0x%lx}\n",
+ ocf_mask[0], ocf_mask[1], ocf_mask[2], ocf_mask[3]);
+}
diff --git a/tools/hcitool.c b/tools/hcitool.c
new file mode 100644
index 00000000..4d6380fb
--- /dev/null
+++ b/tools/hcitool.c
@@ -0,0 +1,368 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <asm/types.h>
+
+#include <bluetooth.h>
+#include <hci.h>
+#include <hci_lib.h>
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+static int ctl;
+
+static int for_each_dev(int flag, int(*func)(int d, long arg), long arg)
+{
+ struct hci_dev_list_req *dl;
+ struct hci_dev_req *dr;
+ int i;
+
+ dl = malloc(HCI_MAX_DEV * sizeof(struct hci_dev_req) + sizeof(uint16_t));
+ if (!dl) {
+ perror("Can't allocate memory");
+ return -1;
+ }
+ dl->dev_num = HCI_MAX_DEV;
+ dr = dl->dev_req;
+
+ if (ioctl(ctl, HCIGETDEVLIST, (void*)dl)) {
+ perror("Can't get device list");
+ return -1;
+ }
+
+ if (!dl->dev_num)
+ return -1;
+
+ for (i=0; i < dl->dev_num; i++, dr++) {
+ if (dr->dev_opt & (1<<flag)) {
+ if (!func || func(dr->dev_id, arg))
+ return dr->dev_id;
+ }
+ }
+ return -1;
+}
+
+static int other_bdaddr(int dev_id, long arg)
+{
+ struct hci_dev_info di = {dev_id: dev_id};
+ if (ioctl(ctl, HCIGETDEVINFO, (void*) &di))
+ return 0;
+ return bacmp((bdaddr_t *)arg, &di.bdaddr);
+}
+
+static int get_route(bdaddr_t *bdaddr)
+{
+ if (bdaddr)
+ return for_each_dev(HCI_UP, other_bdaddr, (long) bdaddr);
+ else
+ return for_each_dev(HCI_UP, NULL, 0);
+}
+
+static int dev_info(int dev_id, long arg)
+{
+ struct hci_dev_info di = {dev_id: dev_id};
+ bdaddr_t bdaddr;
+ if (ioctl(ctl, HCIGETDEVINFO, (void*) &di))
+ return 0;
+
+ baswap(&bdaddr, &di.bdaddr);
+ printf("\t%s\t%s\n", di.name, batostr(&bdaddr));
+ return 0;
+}
+
+static int conn_list(int dev_id, long arg)
+{
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int i;
+
+ if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ cl->dev_id = dev_id;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(ctl, HCIGETCONNLIST, (void*)cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i=0; i < cl->conn_num; i++, ci++) {
+ bdaddr_t bdaddr;
+ baswap(&bdaddr, &ci->bdaddr);
+ printf("\t%s %s %s handle %d state %d lm %s\n",
+ ci->out ? "<" : ">",
+ ci->type == ACL_LINK ? "ACL" : "SCO",
+ batostr(&bdaddr), ci->handle,
+ ci->state,
+ hci_lmtostr(ci->link_mode));
+ }
+ return 0;
+}
+
+static int find_conn(int dev_id, long arg)
+{
+ struct hci_conn_list_req *cl;
+ struct hci_conn_info *ci;
+ int i;
+
+ if (!(cl = malloc(10 * sizeof(*ci) + sizeof(*cl)))) {
+ perror("Can't allocate memory");
+ exit(1);
+ }
+ cl->dev_id = dev_id;
+ cl->conn_num = 10;
+ ci = cl->conn_info;
+
+ if (ioctl(ctl, HCIGETCONNLIST, (void*)cl)) {
+ perror("Can't get connection list");
+ exit(1);
+ }
+
+ for (i=0; i < cl->conn_num; i++, ci++)
+ if (!bacmp((bdaddr_t *)arg, &ci->bdaddr))
+ return 1;
+ return 0;
+}
+
+static void cmd_dev(int dev_id, char **opt, int nopt)
+{
+ printf("Devices:\n");
+ for_each_dev(HCI_UP, dev_info, 0);
+}
+
+static void cmd_inq(int dev_id, char **opt, int nopt)
+{
+ inquiry_info *info;
+ int i, num_rsp = 0, length, flags;
+ bdaddr_t bdaddr;
+
+ if (dev_id < 0)
+ dev_id = get_route(NULL);
+
+ if (nopt >= 1)
+ length = atoi(opt[0]);
+ else
+ length = 10; /* 10 seconds */
+
+ flags = 0;
+ if (nopt >= 2)
+ flags |= !strncasecmp("f", opt[1], 1) ? IREQ_CACHE_FLUSH : 0;
+
+ printf("Inquiring ...\n");
+ info = hci_inquiry(dev_id, length, &num_rsp, NULL, flags);
+
+ if (!info) {
+ perror("Inquiry failed.");
+ exit(1);
+ }
+
+ for (i = 0; i < num_rsp; i++) {
+ baswap(&bdaddr, &(info+i)->bdaddr);
+ printf("\t%s\tclock offset: 0x%4.4x\tclass: 0x%2.2x%2.2x%2.2x\n",
+ batostr(&bdaddr), (info+i)->clock_offset,
+ (info+i)->dev_class[2],
+ (info+i)->dev_class[1],
+ (info+i)->dev_class[0]);
+ }
+ free(info);
+}
+
+static void cmd_con(int dev_id, char **opt, int nopt)
+{
+ printf("Connections:\n");
+ if (dev_id < 0)
+ for_each_dev(HCI_UP, conn_list, 0);
+ else
+ conn_list(dev_id, 0);
+}
+
+static void cmd_cc(int dev_id, char **opt, int nopt)
+{
+ bdaddr_t bdaddr;
+ int dd, ptype, role;
+
+ if (nopt < 1)
+ return;
+
+ baswap(&bdaddr, strtoba(opt[0]));
+
+ if (dev_id < 0) {
+ dev_id = get_route(&bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Device is not available.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ if (nopt >= 2)
+ hci_strtoptype(opt[1], &ptype);
+ else
+ ptype = HCI_DM1 | HCI_DM3 | HCI_DM5 | HCI_DH1 | HCI_DH3 | HCI_DH5;
+
+ if (nopt >= 3)
+ role = !strncasecmp("m", opt[2], 1) ? 0 : 1;
+ else
+ role = 0;
+
+ hci_create_connection(dd, &bdaddr, ptype, role, 1000);
+
+ hci_close_dev(dd);
+}
+
+static void cmd_dc(int dev_id, char **opt, int nopt)
+{
+ struct hci_conn_info_req *cr;
+ bdaddr_t bdaddr;
+ int dd;
+
+ if (nopt < 1)
+ return;
+
+ baswap(&bdaddr, strtoba(*opt));
+
+ if (dev_id < 0) {
+ dev_id = for_each_dev(HCI_UP, find_conn, (long) &bdaddr);
+ if (dev_id < 0) {
+ fprintf(stderr, "Not connected.\n");
+ exit(1);
+ }
+ }
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("HCI device open failed");
+ exit(1);
+ }
+
+ cr = malloc(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (!cr)
+ return;
+
+ bacpy(&cr->bdaddr, &bdaddr);
+ cr->type = ACL_LINK;
+ if (ioctl(dd, HCIGETCONNINFO, (unsigned long) cr) < 0) {
+ perror("Get connection info failed");
+ exit(1);
+ }
+
+ hci_disconnect(dd, cr->conn_info->handle, 0, 100);
+
+ close(dd);
+ free(cr);
+}
+
+struct {
+ char *cmd;
+ void (*func)(int dev_id, char **opt, int nopt);
+ char *opt;
+ char *doc;
+} command[] = {
+ { "dev", cmd_dev, 0, "Display local devices" },
+ { "inq", cmd_inq, "[lenght] [flush]", "Inquire remote devices" },
+ { "con", cmd_con, 0, "Display active connections" },
+ { "cc", cmd_cc, "<bdaddr> [pkt type] [role]", "Create connection to remote device" },
+ { "dc", cmd_dc, "<bdaddr>", "Disconnect from remote device" },
+ { NULL, NULL, 0}
+};
+
+static void usage(void)
+{
+ int i;
+
+ printf("hcitool - HCI Tool\n");
+ printf("Usage:\n"
+ "\thcitool [-i hciX] [command]\n");
+ printf("Commands:\n");
+ for (i=0; command[i].cmd; i++)
+ printf("\t%-4s %-20s\t%s\n", command[i].cmd,
+ command[i].opt ? command[i].opt : " ",
+ command[i].doc);
+}
+
+int main(int argc, char *argv[], char *env[])
+{
+ int opt, i, dev_id = -1;
+ char *dev;
+
+ while ((opt=getopt(argc, argv, "i:h")) != EOF) {
+ switch(opt) {
+ case 'i':
+ dev = strdup(optarg);
+ dev_id = atoi(dev + 3);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (argc - optind < 1) {
+ usage();
+ exit(0);
+ }
+
+ /* Open HCI socket */
+ if ((ctl = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
+ perror("Can't open HCI socket.");
+ exit(1);
+ }
+
+ for (i=0; command[i].cmd; i++) {
+ if (strncmp(command[i].cmd, argv[optind], 3))
+ continue;
+ optind++;
+ command[i].func(dev_id, argv + optind, argc - optind);
+ break;
+ }
+
+ close(ctl);
+ return 0;
+}
diff --git a/tools/l2ping.8 b/tools/l2ping.8
new file mode 100644
index 00000000..41becdba
--- /dev/null
+++ b/tools/l2ping.8
@@ -0,0 +1,49 @@
+.TH L2PING 8 "Jan 22 2002" BlueZ "Linux System Administration"
+.SH NAME
+l2ping \- Send L2CAP echo request and receive answer
+.SH SYNOPSIS
+.B l2ping
+[
+.I -S source addr
+] [
+.I -s size
+] [
+.I -c count
+] [
+.I -f
+] <
+.I bd_addr
+>
+.SH DESCRIPTION
+.LP
+L2ping sends a L2CAP echo request to the Bluetooth MAC address
+.B bd_addr
+given in dotted hex notation.
+.SH OPTIONS
+.TP
+.I -S source addr
+Select address to be used as source address for the request.
+.TP
+.I -s size
+The
+.B size
+of the data packets to be sent.
+.TP
+.I -c count
+Send
+.B count
+number of packets then exit.
+.TP
+.I -f
+Kind of flood ping. Use with care! It reduces the delay time between packets
+to 0.
+.TP
+.I bd_addr
+The Bluetooth MAC address to be pinged in dotted hex notation like
+.B 01:02:03:ab:cd:ef
+or
+.B 01:EF:cd:aB:02:03
+.SH AUTHORS
+Written by Maxim Krasnyansky <maxk@qualcomm.com>
+.PP
+man page by Nils Faerber <nils@kernelconcepts.de>
diff --git a/tools/l2ping.c b/tools/l2ping.c
new file mode 100644
index 00000000..9bb8a4d6
--- /dev/null
+++ b/tools/l2ping.c
@@ -0,0 +1,251 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <netdb.h>
+
+#include <asm/types.h>
+#include <asm/byteorder.h>
+
+#include "bluetooth.h"
+#include "l2cap.h"
+
+/* Defaults */
+bdaddr_t bdaddr;
+int size = 20;
+int ident = 200;
+int delay = 1;
+int count = -1;
+
+/* Stats */
+int sent_pkt = 0, recv_pkt = 0;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)(tv.tv_sec*1000.0) + (float)(tv.tv_usec/1000.0);
+}
+
+static void stat(int sig)
+{
+ int loss = sent_pkt ? (float)((sent_pkt-recv_pkt)/(sent_pkt/100.0)) : 0;
+ printf("%d sent, %d received, %d%% loss\n", sent_pkt, recv_pkt, loss);
+ exit(0);
+}
+
+static void ping(char *svr)
+{
+ struct sockaddr_l2 addr;
+ struct sigaction sa;
+ char buf[2048];
+ int s, i, opt, lost;
+ uint8_t id;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = stat;
+ sigaction(SIGINT, &sa, NULL);
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP)) < 0) {
+ perror("Can't create socket.");
+ exit(1);
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_bdaddr = bdaddr;
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket.");
+ exit(1);
+ }
+
+ baswap(&addr.l2_bdaddr, strtoba(svr));
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ perror("Can't connect.");
+ exit(1);
+ }
+
+ /* Get local address */
+ opt = sizeof(addr);
+ if( getsockname(s, (struct sockaddr *)&addr, &opt) < 0 ) {
+ perror("Can't get local address.");
+ exit(1);
+ }
+ baswap(&bdaddr, &addr.l2_bdaddr);
+
+ printf("Ping: %s from %s (data size %d) ...\n", svr, batostr(&bdaddr), size);
+
+ /* Initialize buffer */
+ for(i = L2CAP_CMD_HDR_SIZE; i < sizeof(buf); i++)
+ buf[i]=(i%40)+'A';
+
+ id = ident;
+
+ while( count == -1 || count-- > 0 ){
+ struct timeval tv_send, tv_recv, tv_diff;
+ l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
+
+ /* Build command header */
+ cmd->code = L2CAP_ECHO_REQ;
+ cmd->ident = id;
+ cmd->len = __cpu_to_le16(size);
+
+ gettimeofday(&tv_send, NULL);
+
+ /* Send Echo Request */
+ if( send(s, buf, size + L2CAP_CMD_HDR_SIZE, 0) <= 0 ){
+ perror("Send failed");
+ exit(1);
+ }
+
+ /* Wait for Echo Response */
+ lost = 0;
+ while( 1 ) {
+ struct pollfd pf[1];
+ register int err;
+
+ pf[0].fd = s; pf[0].events = POLLIN;
+ if( (err = poll(pf, 1, 10*1000)) < 0 ) {
+ perror("Poll failed");
+ exit(1);
+ }
+
+ if( !err ){
+ lost = 1;
+ break;
+ }
+
+ if( (err = recv(s, buf, sizeof(buf), 0)) < 0 ) {
+ perror("Recv failed");
+ exit(1);
+ }
+
+ if( !err ){
+ printf("Disconnected\n");
+ exit(1);
+ }
+
+ cmd->len = __le16_to_cpu(cmd->len);
+
+ /* Check for our id */
+ if( cmd->ident != id )
+ continue;
+
+ /* Check type */
+ if( cmd->code == L2CAP_ECHO_RSP )
+ break;
+ if( cmd->code == L2CAP_COMMAND_REJ ){
+ printf("Peer doesn't support Echo packets\n");
+ exit(1);
+ }
+
+ }
+ sent_pkt++;
+
+ if( !lost ){
+ recv_pkt++;
+
+ gettimeofday(&tv_recv, NULL);
+ timersub(&tv_recv, &tv_send, &tv_diff);
+
+ printf("%d bytes from %s id %d time %.2fms\n", cmd->len, svr, id, tv2fl(tv_diff));
+
+ if( delay ) sleep(delay);
+ } else {
+ printf("no response from %s: id %d\n", svr, id);
+ }
+
+ if( ++id > 254 ) id = ident;
+ }
+ stat(0);
+}
+
+static void usage(void)
+{
+ printf("l2ping - L2CAP ping\n");
+ printf("Usage:\n");
+ printf("\tl2ping [-S source addr] [-s size] [-c count] [-f] <bd_addr>\n");
+}
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+int main(int argc, char *argv[])
+{
+ register int opt;
+
+ /* Default options */
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt(argc,argv,"s:c:fS:")) != EOF) {
+ switch(opt) {
+ case 'f':
+ /* Kinda flood ping */
+ delay = 0;
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ break;
+
+ case 's':
+ size = atoi(optarg);
+ break;
+
+ case 'S':
+ baswap(&bdaddr, strtoba(optarg));
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (!(argc - optind)) {
+ usage();
+ exit(1);
+ }
+
+ ping(argv[optind]);
+
+ return 0;
+}
diff --git a/tools/l2test.c b/tools/l2test.c
new file mode 100644
index 00000000..be0b59e1
--- /dev/null
+++ b/tools/l2test.c
@@ -0,0 +1,493 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include <bluetooth.h>
+#include <l2cap.h>
+
+/* Test modes */
+enum {
+ SEND,
+ RECV,
+ RECONNECT,
+ MULTY,
+ DUMP,
+ CONNECT
+};
+
+unsigned char *buf;
+
+/* Default mtu */
+int imtu = 672;
+int omtu = 0;
+
+/* Default data size */
+long data_size = 672;
+
+/* Default addr and psm */
+bdaddr_t bdaddr;
+unsigned short psm = 10;
+
+int master = 0;
+int auth = 0;
+int encrypt = 0;
+
+float tv2fl(struct timeval tv)
+{
+ return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+int do_connect(char *svr)
+{
+ struct sockaddr_l2 rem_addr, loc_addr;
+ struct l2cap_options opts;
+ int s, opt;
+
+ if( (s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0 ) {
+ syslog(LOG_ERR, "Can't create socket. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&loc_addr, 0, sizeof(loc_addr));
+ loc_addr.l2_family = AF_BLUETOOTH;
+ loc_addr.l2_bdaddr = bdaddr;
+ if( bind(s, (struct sockaddr *) &loc_addr, sizeof(loc_addr)) < 0 ) {
+ syslog(LOG_ERR, "Can't bind socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Get default options */
+ opt = sizeof(opts);
+ if( getsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0 ) {
+ syslog(LOG_ERR, "Can't get default L2CAP options. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ /* Set new options */
+ opts.omtu = omtu;
+ opts.imtu = imtu;
+ if( setsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0 ) {
+ syslog(LOG_ERR, "Can't set L2CAP options. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&rem_addr, 0, sizeof(rem_addr));
+ rem_addr.l2_family = AF_BLUETOOTH;
+ baswap(&rem_addr.l2_bdaddr, strtoba(svr));
+ rem_addr.l2_psm = htobs(psm);
+ if( connect(s, (struct sockaddr *)&rem_addr, sizeof(rem_addr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ opt = sizeof(opts);
+ if( getsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0 ){
+ syslog(LOG_ERR, "Can't get L2CAP options. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ syslog(LOG_INFO, "Connected [imtu %d, omtu %d, flush_to %d]\n",
+ opts.imtu, opts.omtu, opts.flush_to);
+
+ return s;
+}
+
+void do_listen( void (*handler)(int sk) )
+{
+ struct sockaddr_l2 loc_addr, rem_addr;
+ struct l2cap_options opts;
+ int s, s1, opt;
+ bdaddr_t ba;
+
+ if( (s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP)) < 0 ) {
+ syslog(LOG_ERR, "Can't create socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ loc_addr.l2_family = AF_BLUETOOTH;
+ loc_addr.l2_bdaddr = bdaddr;
+ loc_addr.l2_psm = htobs(psm);
+ if( bind(s, (struct sockaddr *) &loc_addr, sizeof(loc_addr)) < 0 ) {
+ syslog(LOG_ERR, "Can't bind socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ if( listen(s, 10) ) {
+ syslog(LOG_ERR,"Can not listen on the socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Get default options */
+ opt = sizeof(opts);
+ if (getsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0) {
+ syslog(LOG_ERR, "Can't get default L2CAP options. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Set new options */
+ opts.imtu = imtu;
+ if (setsockopt(s, SOL_L2CAP, L2CAP_OPTIONS, &opts, opt) < 0) {
+ syslog(LOG_ERR, "Can't set L2CAP options. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+
+ /* Set link mode */
+ opt = 0;
+ if (master)
+ opt |= L2CAP_LM_MASTER;
+
+ if (auth)
+ opt |= L2CAP_LM_AUTH;
+
+ if (encrypt)
+ opt |= L2CAP_LM_ENCRYPT;
+
+ if (setsockopt(s, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't set L2CAP link mode. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ syslog(LOG_INFO,"Waiting for connection on psm %d ...", psm);
+
+ while(1) {
+ opt = sizeof(rem_addr);
+ if( (s1 = accept(s, (struct sockaddr *)&rem_addr, &opt)) < 0 ) {
+ syslog(LOG_ERR,"Accept failed. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+ if( fork() ) {
+ /* Parent */
+ close(s1);
+ continue;
+ }
+ /* Child */
+
+ close(s);
+
+ opt = sizeof(opts);
+ if( getsockopt(s1, SOL_L2CAP, L2CAP_OPTIONS, &opts, &opt) < 0 ) {
+ syslog(LOG_ERR, "Can't get L2CAP options. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ baswap(&ba, &rem_addr.l2_bdaddr);
+ syslog(LOG_INFO, "Connect from %s [imtu %d, omtu %d, flush_to %d]\n",
+ batostr(&ba), opts.imtu, opts.omtu, opts.flush_to);
+
+ handler(s1);
+
+ syslog(LOG_INFO, "Disconnect\n");
+ exit(0);
+ }
+}
+
+void dump_mode(int s)
+{
+ int len;
+
+ syslog(LOG_INFO,"Receiving ...");
+ while ((len = read(s, buf, data_size)) > 0)
+ syslog(LOG_INFO, "Recevied %d bytes\n", len);
+}
+
+void recv_mode(int s)
+{
+ struct timeval tv_beg,tv_end,tv_diff;
+ long total;
+ uint32_t seq;
+
+ syslog(LOG_INFO,"Receiving ...");
+
+ seq = 0;
+ while (1) {
+ gettimeofday(&tv_beg,NULL);
+ total = 0;
+ while (total < data_size) {
+ uint32_t sq;
+ uint16_t l;
+ int i,r;
+
+ if ((r = recv(s, buf, data_size, 0)) <= 0) {
+ if (r < 0)
+ syslog(LOG_ERR, "Read failed. %s(%d)",
+ strerror(errno), errno);
+ return;
+ }
+
+ /* Check sequence */
+ sq = btohl(*(uint32_t *)buf);
+ if (seq != sq) {
+ syslog(LOG_INFO, "seq missmatch: %d -> %d", seq, sq);
+ seq = sq;
+ }
+ seq++;
+
+ /* Check length */
+ l = btohs(*(uint16_t *)(buf+4));
+ if (r != l) {
+ syslog(LOG_INFO, "size missmatch: %d -> %d", r, l);
+ continue;
+ }
+
+ /* Verify data */
+ for (i=6; i < r; i++) {
+ if (buf[i] != 0x7f)
+ syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
+ }
+
+ total += r;
+ }
+ gettimeofday(&tv_end,NULL);
+
+ timersub(&tv_end,&tv_beg,&tv_diff);
+
+ syslog(LOG_INFO,"%ld bytes in %.2f sec, %.2f kB/s",total,
+ tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+ }
+}
+
+void send_mode(char *svr)
+{
+ uint32_t seq;
+ int s, i;
+
+ if( (s = do_connect(svr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ syslog(LOG_INFO,"Sending ...");
+
+ for(i=6; i < data_size; i++)
+ buf[i]=0x7f;
+
+ seq = 0;
+ while(1){
+ *(uint32_t *)buf = htobl(seq++);
+ *(uint16_t *)(buf+4) = htobs(data_size);
+
+ if( send(s, buf, data_size, 0) <= 0 ) {
+ syslog(LOG_ERR, "Send failed. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+ }
+}
+
+void reconnect_mode(char *svr)
+{
+ while(1){
+ int s;
+ if( (s = do_connect(svr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+ close(s);
+
+ usleep(10);
+ }
+}
+
+void connect_mode(char *svr)
+{
+ int s;
+ if ((s = do_connect(svr)) < 0) {
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+ sleep(99999999);
+}
+
+void multy_connect_mode(char *svr)
+{
+ while(1){
+ int i, s;
+ for(i=0; i<10; i++){
+ if( fork() ) continue;
+
+ /* Child */
+ if( (s = do_connect(svr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)", strerror(errno), errno);
+ }
+ close(s);
+ exit(0);
+ }
+ sleep(19);
+ }
+}
+
+void usage(void)
+{
+ printf("l2test - L2CAP testing\n"
+ "Usage:\n");
+ printf("\tl2test <mode> [-b bytes] [-S bd_addr] [-P psm] [-I imtu] [-O omtu] [-M] [bd_addr]\n");
+ printf("Modes:\n"
+ "\t-d dump (server)\n"
+ "\t-n silent connect (client)\n"
+ "\t-c reconnect (client)\n"
+ "\t-m multiple connects (client)\n"
+ "\t-r receive (server)\n"
+ "\t-s send (client)\n");
+}
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+int main(int argc ,char *argv[])
+{
+ struct sigaction sa;
+ int opt, mode = RECV;
+
+ while ((opt=getopt(argc,argv,"rdscmnb:P:I:O:S:MAE")) != EOF) {
+ switch(opt) {
+ case 'r':
+ mode = RECV;
+ break;
+
+ case 's':
+ mode = SEND;
+ break;
+
+ case 'd':
+ mode = DUMP;
+ break;
+
+ case 'c':
+ mode = RECONNECT;
+ break;
+
+ case 'n':
+ mode = CONNECT;
+ break;
+
+ case 'm':
+ mode = MULTY;
+ break;
+
+ case 'b':
+ data_size = atoi(optarg);
+ break;
+
+ case 'S':
+ baswap(&bdaddr, strtoba(optarg));
+ break;
+
+ case 'P':
+ psm = atoi(optarg);
+ break;
+
+ case 'I':
+ imtu = atoi(optarg);
+ break;
+
+ case 'O':
+ omtu = atoi(optarg);
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encrypt = 1;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (!(argc - optind) && (mode!=RECV && mode !=DUMP)) {
+ usage();
+ exit(1);
+ }
+
+ if (!(buf = malloc(data_size))) {
+ perror("Can't allocate data buffer");
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &sa, NULL);
+
+ openlog("l2test", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+ switch( mode ){
+ case RECV:
+ do_listen(recv_mode);
+ break;
+
+ case DUMP:
+ do_listen(dump_mode);
+ break;
+
+ case SEND:
+ send_mode(argv[optind]);
+ break;
+
+ case RECONNECT:
+ reconnect_mode(argv[optind]);
+ break;
+
+ case MULTY:
+ multy_connect_mode(argv[optind]);
+ break;
+
+ case CONNECT:
+ connect_mode(argv[optind]);
+ break;
+ }
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}
diff --git a/tools/scotest.c b/tools/scotest.c
new file mode 100644
index 00000000..4b56d081
--- /dev/null
+++ b/tools/scotest.c
@@ -0,0 +1,358 @@
+/*
+ BlueZ - Bluetooth protocol stack for Linux
+ Copyright (C) 2000-2001 Qualcomm Incorporated
+
+ Written 2000,2001 by Maxim Krasnyansky <maxk@qualcomm.com>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License version 2 as
+ published by the Free Software Foundation;
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
+ OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
+ RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
+ USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
+ TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
+*/
+
+/*
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include <bluetooth.h>
+#include <sco.h>
+
+/* Test modes */
+enum {
+ SEND,
+ RECV,
+ RECONNECT,
+ MULTY,
+ DUMP
+};
+
+unsigned char *buf;
+
+/* Default data size */
+long data_size = 672;
+
+bdaddr_t bdaddr;
+
+float tv2fl(struct timeval tv)
+{
+ return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+int do_connect(char *svr)
+{
+ struct sockaddr_sco rem_addr, loc_addr;
+ int s;
+
+ if( (s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0 ) {
+ syslog(LOG_ERR, "Can't create socket. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&loc_addr, 0, sizeof(loc_addr));
+ loc_addr.sco_family = AF_BLUETOOTH;
+ loc_addr.sco_bdaddr = bdaddr;
+ if( bind(s, (struct sockaddr *) &loc_addr, sizeof(loc_addr)) < 0 ) {
+ syslog(LOG_ERR, "Can't bind socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ memset(&rem_addr, 0, sizeof(rem_addr));
+ rem_addr.sco_family = AF_BLUETOOTH;
+ baswap(&rem_addr.sco_bdaddr, strtoba(svr));
+ if( connect(s, (struct sockaddr *)&rem_addr, sizeof(rem_addr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect. %s(%d)", strerror(errno), errno);
+ return -1;
+ }
+
+ syslog(LOG_INFO, "Connected\n");
+
+ return s;
+}
+
+void do_listen( void (*handler)(int sk) )
+{
+ struct sockaddr_sco loc_addr, rem_addr;
+ int s, s1, opt;
+ bdaddr_t ba;
+
+ if( (s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0 ) {
+ syslog(LOG_ERR, "Can't create socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ loc_addr.sco_family = AF_BLUETOOTH;
+ loc_addr.sco_bdaddr = bdaddr;
+ if( bind(s, (struct sockaddr *) &loc_addr, sizeof(loc_addr)) < 0 ) {
+ syslog(LOG_ERR, "Can't bind socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ if( listen(s, 10) ) {
+ syslog(LOG_ERR,"Can not listen on the socket. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ syslog(LOG_INFO,"Waiting for connection ...");
+
+ while(1) {
+ opt = sizeof(rem_addr);
+ if( (s1 = accept(s, (struct sockaddr *)&rem_addr, &opt)) < 0 ) {
+ syslog(LOG_ERR,"Accept failed. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+ if( fork() ) {
+ /* Parent */
+ close(s1);
+ continue;
+ }
+ /* Child */
+
+ close(s);
+
+ baswap(&ba, &rem_addr.sco_bdaddr);
+ syslog(LOG_INFO, "Connect from %s\n", batostr(&ba));
+
+ handler(s1);
+
+ syslog(LOG_INFO, "Disconnect\n");
+ exit(0);
+ }
+}
+
+void dump_mode(int s)
+{
+ int len;
+
+ syslog(LOG_INFO,"Receiving ...");
+ while ((len = read(s, buf, data_size)) > 0)
+ syslog(LOG_INFO, "Recevied %d bytes\n", len);
+}
+
+void recv_mode(int s)
+{
+ struct timeval tv_beg,tv_end,tv_diff;
+ long total;
+ uint32_t seq;
+
+ syslog(LOG_INFO, "Receiving ...");
+
+ seq = 0;
+ while (1) {
+ gettimeofday(&tv_beg,NULL);
+ total = 0;
+ while (total < data_size) {
+ int r;
+ if ((r = recv(s, buf, data_size, 0)) <= 0) {
+ if (r < 0)
+ syslog(LOG_ERR, "Read failed. %s(%d)",
+ strerror(errno), errno);
+ return;
+ }
+ total += r;
+ }
+ gettimeofday(&tv_end,NULL);
+
+ timersub(&tv_end,&tv_beg,&tv_diff);
+
+ syslog(LOG_INFO,"%ld bytes in %.2fm speed %.2f kb",total,
+ tv2fl(tv_diff) / 60.0,
+ (float)( total / tv2fl(tv_diff) ) / 1024.0 );
+ }
+}
+
+void send_mode(char *svr)
+{
+ struct sco_options so;
+ uint32_t seq;
+ int s, i, opt;
+
+ if ((s = do_connect(svr)) < 0) {
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ opt = sizeof(so);
+ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &so, &opt) < 0) {
+ syslog(LOG_ERR, "Can't get SCO options. %s(%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+
+ syslog(LOG_INFO,"Sending ...");
+
+ for (i=6; i < so.mtu; i++)
+ buf[i]=0x7f;
+
+ seq = 0;
+ while (1) {
+ *(uint32_t *)buf = htobl(seq++);
+ *(uint16_t *)(buf+4) = htobs(data_size);
+
+ if (send(s, buf, so.mtu, 0) <= 0) {
+ syslog(LOG_ERR, "Send failed. %s(%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+ usleep(1);
+ }
+}
+
+void reconnect_mode(char *svr)
+{
+ while(1){
+ int s;
+ if( (s = do_connect(svr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)", strerror(errno), errno);
+ exit(1);
+ }
+ close(s);
+
+ sleep(5);
+ }
+}
+
+void multy_connect_mode(char *svr)
+{
+ while(1){
+ int i, s;
+ for(i=0; i<10; i++){
+ if( fork() ) continue;
+
+ /* Child */
+ if( (s = do_connect(svr)) < 0 ){
+ syslog(LOG_ERR, "Can't connect to the server. %s(%d)", strerror(errno), errno);
+ }
+ close(s);
+ exit(0);
+ }
+ sleep(19);
+ }
+}
+
+void usage(void)
+{
+ printf("scotest - SCO testing\n"
+ "Usage:\n");
+ printf("\tscotest <mode> [-b bytes] [bd_addr]\n");
+ printf("Modes:\n"
+ "\t-d dump (server)\n"
+ "\t-c reconnect (client)\n"
+ "\t-m multiple connects (client)\n"
+ "\t-r receive (server)\n"
+ "\t-s send (client)\n");
+}
+
+extern int optind,opterr,optopt;
+extern char *optarg;
+
+int main(int argc ,char *argv[])
+{
+ struct sigaction sa;
+ int opt, mode = RECV;
+
+ while ((opt=getopt(argc,argv,"rdscmb:")) != EOF) {
+ switch(opt) {
+ case 'r':
+ mode = RECV;
+ break;
+
+ case 's':
+ mode = SEND;
+ break;
+
+ case 'd':
+ mode = DUMP;
+ break;
+
+ case 'c':
+ mode = RECONNECT;
+ break;
+
+ case 'm':
+ mode = MULTY;
+ break;
+
+ case 'b':
+ data_size = atoi(optarg);
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (!(argc - optind) && (mode!=RECV && mode !=DUMP)) {
+ usage();
+ exit(1);
+ }
+
+ if (!(buf = malloc(data_size))) {
+ perror("Can't allocate data buffer");
+ exit(1);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_NOCLDSTOP;
+ sigaction(SIGCHLD, &sa, NULL);
+
+ openlog("scotest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+ switch( mode ){
+ case RECV:
+ do_listen(recv_mode);
+ break;
+
+ case DUMP:
+ do_listen(dump_mode);
+ break;
+
+ case SEND:
+ send_mode(argv[optind]);
+ break;
+
+ case RECONNECT:
+ reconnect_mode(argv[optind]);
+ break;
+
+ case MULTY:
+ multy_connect_mode(argv[optind]);
+ break;
+ }
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}