summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.am44
-rwxr-xr-xtest/apitest448
-rw-r--r--test/attest.c183
-rw-r--r--test/auth-agent.c351
-rw-r--r--test/bdaddr.868
-rw-r--r--test/bdaddr.c460
-rw-r--r--test/dbusdef.py59
-rw-r--r--test/hciemu.c1346
-rwxr-xr-xtest/hsmicro20
-rwxr-xr-xtest/hsplay22
-rw-r--r--test/hstest.c308
-rw-r--r--test/l2test.c1104
-rw-r--r--test/lmptest.c175
-rw-r--r--test/passkey-agent.c418
-rw-r--r--test/rctest.c688
-rw-r--r--test/scotest.c434
-rw-r--r--test/sdptest.c146
17 files changed, 6274 insertions, 0 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 00000000..16b80e7d
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,44 @@
+
+if TEST
+sbin_PROGRAMS = hciemu
+
+bin_PROGRAMS = l2test rctest
+
+noinst_PROGRAMS = sdptest scotest attest hstest bdaddr lmptest \
+ passkey-agent auth-agent
+
+hciemu_LDADD = $(top_builddir)/common/libhelper.a \
+ @GLIB_LIBS@ @BLUEZ_LIBS@
+
+l2test_LDADD = @BLUEZ_LIBS@
+
+rctest_LDADD = @BLUEZ_LIBS@
+
+sdptest_LDADD = @BLUEZ_LIBS@
+
+scotest_LDADD = @BLUEZ_LIBS@
+
+attest_LDADD = @BLUEZ_LIBS@
+
+hstest_LDADD = @BLUEZ_LIBS@
+
+bdaddr_SOURCES = bdaddr.c
+
+bdaddr_LDADD = @BLUEZ_LIBS@ $(top_builddir)/common/libhelper.a
+
+lmptest_LDADD = @BLUEZ_LIBS@
+
+passkey_agent_LDADD = @DBUS_LIBS@
+
+auth_agent_LDADD = @DBUS_LIBS@
+
+noinst_MANS = bdaddr.8
+
+AM_CFLAGS = @BLUEZ_CFLAGS@ @DBUS_CFLAGS@ @GLIB_CFLAGS@
+endif
+
+INCLUDES = -I$(top_srcdir)/common
+
+EXTRA_DIST = apitest hsplay hsmicro bdaddr.8 dbusdef.py
+
+MAINTAINERCLEANFILES = Makefile.in
diff --git a/test/apitest b/test/apitest
new file mode 100755
index 00000000..b1c3f103
--- /dev/null
+++ b/test/apitest
@@ -0,0 +1,448 @@
+#!/usr/bin/env python
+
+import dbus
+import dbus.decorators
+import dbus.glib
+import gobject
+import sys
+import getopt
+from signal import *
+
+mgr_cmds = [ "InterfaceVersion", "ListAdapters", "DefaultAdapter" ]
+mgr_signals = [ "AdapterAdded", "AdapterRemoved" ]
+
+dev_cmds = [ "GetAddress",
+ "GetVersion",
+ "GetRevision",
+ "GetManufacturer",
+ "GetCompany",
+ "GetMode",
+ "SetMode",
+ "GetDiscoverableTimeout",
+ "SetDiscoverableTimeout",
+ "IsConnectable",
+ "IsDiscoverable",
+ "IsConnected",
+ "ListConnections",
+ "GetMajorClass",
+ "ListAvailableMinorClasses",
+ "GetMinorClass",
+ "SetMinorClass",
+ "GetServiceClasses",
+ "GetName",
+ "SetName",
+ "GetRemoteVersion",
+ "GetRemoteRevision",
+ "GetRemoteManufacturer",
+ "GetRemoteCompany",
+ "GetRemoteMajorClass",
+ "GetRemoteMinorClass",
+ "GetRemoteServiceClasses",
+ "GetRemoteClass",
+ "GetRemoteName",
+ "GetRemoteAlias",
+ "SetRemoteAlias",
+ "ClearRemoteAlias",
+ "LastSeen",
+ "LastUsed",
+ "DisconnectRemoteDevice",
+ "CreateBonding",
+ "CancelBondingProcess",
+ "RemoveBonding",
+ "HasBonding",
+ "ListBondings",
+ "GetPinCodeLength",
+ "GetEncryptionKeySize",
+ "DiscoverDevices",
+ "DiscoverDevicesWithoutNameResolving",
+ "CancelDiscovery",
+ "ListRemoteDevices",
+ "ListRecentRemoteDevices" ]
+dev_signals = [ "ModeChanged",
+ "NameChanged",
+ "MinorClassChanged",
+ "DiscoveryStarted",
+ "DiscoveryCompleted",
+ "RemoteDeviceFound",
+ "RemoteNameUpdated",
+ "RemoteNameFailed",
+ "RemoteAliasChanged"
+ "RemoteAliasCleared",
+ "RemoteDeviceConnected",
+ "RemoteDeviceDisconnectRequested",
+ "RemoteDeviceDisconnected",
+ "BondingCreated",
+ "BondingRemoved" ]
+
+dev_signals_filter = [ "/org/bluez/hci0", "/org/bluez/hci1",
+ "/org/bluez/hci2", "/org/bluez/hci3",
+ "/org/bluez/hci4", "/org/bluez/hci5",
+ "/org/bluez/hci6", "/org/bluez/hci7" ]
+
+class Tester:
+ exit_events = []
+ dev_path = None
+ need_dev = False
+ listen = False
+ at_interrupt = None
+
+ def __init__(self, argv):
+ self.name = argv[0]
+
+ self.parse_args(argv[1:])
+
+ try:
+ self.dbus_setup()
+ except dbus.DBusException, e:
+ print 'Failed to do D-Bus setup: %s' % e
+ sys.exit(1)
+
+ def parse_args(self, argv):
+ try:
+ opts, args = getopt.getopt(argv, "hli:")
+ except getopt.GetoptError:
+ self.usage()
+ sys.exit(1)
+
+ for o, a in opts:
+ if o == "-h":
+ self.usage()
+ sys.exit()
+ elif o == "-l":
+ self.listen = True
+ elif o == "-i":
+ if a[0] == '/':
+ self.dev_path = a
+ else:
+ self.dev_path = '/org/bluez/%s' % a
+
+ if not (args or self.listen):
+ self.usage()
+ sys.exit(1)
+
+ if args:
+ self.cmd = args[0]
+ self.cmd_args = args[1:]
+
+ def dbus_dev_setup(self):
+ if not self.dev_path:
+ try:
+ self.dbus_mgr_setup()
+ self.dev_path = self.manager.DefaultAdapter()
+ except dbus.DBusException, e:
+ print 'Failed to get default device: %s' % e
+ sys.exit(1)
+ try:
+ obj = self.bus.get_object('org.bluez', self.dev_path)
+ self.device = dbus.Interface(obj, 'org.bluez.Adapter')
+ except dbus.DBusException, e:
+ print 'Failed to setup device path: %s' % e
+ sys.exit(1)
+
+ def dbus_dev_sig_setup(self):
+ try:
+ for signal in dev_signals:
+ for path in dev_signals_filter:
+ self.bus.add_signal_receiver(self.dev_signal_handler,
+ signal, 'org.bluez.Adapter',
+ 'org.bluez', path,
+ message_keyword='dbus_message')
+ except dbus.DBusException, e:
+ print 'Failed to setup signal handler for device path: %s' % e
+ sys.exit(1)
+
+ def dbus_mgr_sig_setup(self):
+ try:
+ for signal in mgr_signals:
+ self.bus.add_signal_receiver(self.mgr_signal_handler,
+ signal,'org.bluez.Manager',
+ 'org.bluez', '/org/bluez')
+ except dbus.DBusException, e:
+ print 'Failed to setup signal handler for manager path: %s' % e
+ sys.exit(1)
+
+ def dbus_mgr_setup(self):
+ self.manager_obj = self.bus.get_object('org.bluez', '/org/bluez')
+ self.manager = dbus.Interface(self.manager_obj, 'org.bluez.Manager')
+
+ def dbus_setup(self):
+ self.bus = dbus.SystemBus()
+
+ def usage(self):
+ print 'Usage: %s [-i <dev>] [-l] [-h] <cmd> [arg1..]' % self.name
+ print ' -i <dev> Specify device (e.g. "hci0" or "/org/bluez/hci0")'
+ print ' -l Listen for events (no command required)'
+ print ' -h Show this help'
+ print 'Manager commands:'
+ for cmd in mgr_cmds:
+ print '\t%s' % cmd
+ print 'Adapter commands:'
+ for cmd in dev_cmds:
+ print '\t%s' % cmd
+
+ #@dbus.decorators.explicitly_pass_message
+ def dev_signal_handler(*args, **keywords):
+ dbus_message = keywords["dbus_message"]
+ print '%s - %s: ' % (dbus_message.get_member(), dbus_message.get_path()),
+ for arg in args[1:]:
+ print '%s ' % arg,
+ print
+
+ #@dbus.decorators.explicitly_pass_message
+ def mgr_signal_handler(*args, **keywords):
+ dbus_message = keywords["dbus_message"]
+ print '%s: ' % dbus_message.get_member()
+ for arg in args[1:]:
+ print '%s ' % arg,
+ print
+
+ def signal_cb(self, sig, frame):
+ print 'Caught signal, exiting'
+ if self.at_interrupt:
+ self.at_interrupt()
+ self.main_loop.quit()
+
+ def call_mgr_dbus_func(self):
+ if self.cmd == 'InterfaceVersion':
+ try:
+ print self.manager.InterfaceVersion()
+ except dbus.DBusException, e:
+ print 'Sending %s failed: %s' % (self.cmd, e)
+ if self.cmd == 'ListAdapters':
+ try:
+ devices = self.manager.ListAdapters()
+ except dbus.DBusException, e:
+ print 'Sending %s failed: %s' % (self.cmd, e)
+ sys.exit(1)
+ for device in devices:
+ print device
+ elif self.cmd == 'DefaultAdapter':
+ try:
+ print self.manager.DefaultAdapter()
+ except dbus.DBusException, e:
+ print 'Sending %s failed: %s' % (self.cmd, e)
+ sys.exit(1)
+
+ def call_dev_dbus_func(self):
+ try:
+ if self.cmd == 'GetAddress':
+ print self.device.GetAddress()
+ elif self.cmd == 'GetManufacturer':
+ print self.device.GetManufacturer()
+ elif self.cmd == 'GetVersion':
+ print self.device.GetVersion()
+ elif self.cmd == 'GetRevision':
+ print self.device.GetRevision()
+ elif self.cmd == 'GetCompany':
+ print self.device.GetCompany()
+ elif self.cmd == 'GetMode':
+ print self.device.GetMode()
+ elif self.cmd == 'SetMode':
+ if len(self.cmd_args) == 1:
+ self.device.SetMode(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> SetMode scan_mode' % self.name
+ elif self.cmd == 'GetDiscoverableTimeout':
+ print '%u' % (self.device.GetDiscoverableTimeout())
+ elif self.cmd == 'SetDiscoverableTimeout':
+ if len(self.cmd_args) == 1:
+ self.device.SetDiscoverableTimeout(dbus.UInt32(self.cmd_args[0]))
+ else:
+ print 'Usage: %s -i <dev> SetDiscoverableTimeout timeout' % self.name
+ elif self.cmd == 'IsConnectable':
+ print self.device.IsConnectable()
+ elif self.cmd == 'IsDiscoverable':
+ print self.device.IsDiscoverable()
+ elif self.cmd == 'IsConnected':
+ if len(self.cmd_args) == 1:
+ print self.device.IsConnected(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> IsConnected address' % self.name
+ elif self.cmd == 'ListConnections':
+ print self.device.ListConnections()
+ elif self.cmd == 'GetMajorClass':
+ print self.device.GetMajorClass()
+ elif self.cmd == 'ListAvailableMinorClasses':
+ print self.device.ListAvailableMinorClasses()
+ elif self.cmd == 'GetMinorClass':
+ print self.device.GetMinorClass()
+ elif self.cmd == 'SetMinorClass':
+ if len(self.cmd_args) == 1:
+ self.device.SetMinorClass(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> SetMinorClass minor' % self.name
+ elif self.cmd == 'GetServiceClasses':
+ classes = self.device.GetServiceClasses()
+ for clas in classes:
+ print clas,
+ elif self.cmd == 'GetName':
+ print self.device.GetName()
+ elif self.cmd == 'SetName':
+ if len(self.cmd_args) == 1:
+ self.device.SetName(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> SetName newname' % self.name
+ elif self.cmd == 'GetRemoteName':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteName(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteName address' % self.name
+ elif self.cmd == 'GetRemoteVersion':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteVersion(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteVersion address' % self.name
+ elif self.cmd == 'GetRemoteRevision':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteRevision(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteRevision address' % self.name
+ elif self.cmd == 'GetRemoteManufacturer':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteManufacturer(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteManufacturer address' % self.name
+ elif self.cmd == 'GetRemoteCompany':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteCompany(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteCompany address' % self.name
+ elif self.cmd == 'GetRemoteAlias':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteAlias(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteAlias address' % self.name
+ elif self.cmd == 'GetRemoteMajorClass':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteMajorClass(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteMajorClass address' % self.name
+ elif self.cmd == 'GetRemoteMinorClass':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteMinorClass(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteMinorClass address' % self.name
+ elif self.cmd == 'GetRemoteServiceClasses':
+ if len(self.cmd_args) == 1:
+ print self.device.GetRemoteServiceClasses(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetRemoteServiceClasses address' % self.name
+ elif self.cmd == 'SetRemoteAlias':
+ if len(self.cmd_args) == 2:
+ self.device.SetRemoteAlias(self.cmd_args[0], self.cmd_args[1])
+ else:
+ print 'Usage: %s -i <dev> SetRemoteAlias address alias' % self.name
+ elif self.cmd == 'ClearRemoteAlias':
+ if len(self.cmd_args) == 1:
+ print self.device.ClearRemoteAlias(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> ClearRemoteAlias address' % self.name
+ elif self.cmd == 'LastSeen':
+ if len(self.cmd_args) == 1:
+ print self.device.LastSeen(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> LastSeen address' % self.name
+ elif self.cmd == 'LastUsed':
+ if len(self.cmd_args) == 1:
+ print self.device.LastUsed(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> LastUsed address' % self.name
+ elif self.cmd == 'DisconnectRemoteDevice':
+ if len(self.cmd_args) == 1:
+ print self.device.LastUsed(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> DisconnectRemoteDevice address' % self.name
+ elif self.cmd == 'CreateBonding':
+ if len(self.cmd_args) == 1:
+ print self.device.CreateBonding(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> CreateBonding address' % self.name
+ elif self.cmd == 'RemoveBonding':
+ if len(self.cmd_args) == 1:
+ print self.device.RemoveBonding(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> RemoveBonding address' % self.name
+ elif self.cmd == 'CancelBondingProcess':
+ if len(self.cmd_args) == 1:
+ print self.device.CancelBondingProcess(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> CancelBondingProcess address' % self.name
+ elif self.cmd == 'HasBonding':
+ if len(self.cmd_args) == 1:
+ print self.device.HasBonding(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> HasBonding address' % self.name
+ elif self.cmd == 'ListBondings':
+ bondings = self.device.ListBondings()
+ for bond in bondings:
+ print bond,
+ elif self.cmd == 'GetPinCodeLength':
+ if len(self.cmd_args) == 1:
+ print self.device.GetPinCodeLength(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetPinCodeLength address' % self.name
+ elif self.cmd == 'GetEncryptionKeySize':
+ if len(self.cmd_args) == 1:
+ print self.device.GetEncryptionKeySize(self.cmd_args[0])
+ else:
+ print 'Usage: %s -i <dev> GetEncryptionKeySize address' % self.name
+ elif self.cmd == 'DiscoverDevices':
+ print self.device.DiscoverDevices()
+ elif self.cmd == 'DiscoverDevicesWithoutNameResolving':
+ print self.device.DiscoverDevicesWithoutNameResolving()
+ elif self.cmd == 'ListRemoteDevices':
+ devices = self.device.ListRemoteDevices()
+ for device in devices:
+ print device,
+ elif self.cmd == 'ListRecentRemoteDevices':
+ if len(self.cmd_args) == 1:
+ devices = self.device.ListRecentRemoteDevices(self.cmd_args[0])
+ for device in devices:
+ print device,
+ else:
+ print 'Usage: %s -i <dev> ListRecentRemoteDevices date' % self.name
+ else:
+ # FIXME: remove at future version
+ print 'Script Error: Method %s not found. Maybe a mispelled word.' % (self.cmd_args)
+ except dbus.DBusException, e:
+ print '%s failed: %s' % (self.cmd, e)
+ sys.exit(1)
+
+ def run(self):
+ # Manager methods
+ if self.listen:
+ self.dbus_mgr_sig_setup()
+ self.dbus_dev_sig_setup()
+ print 'Listening for events...'
+
+ if self.cmd in mgr_cmds:
+ try:
+ self.dbus_mgr_setup()
+ except dbus.DBusException, e:
+ print 'Failed to setup manager interface: %s' % e
+ sys.exit(1)
+ self.call_mgr_dbus_func()
+ elif self.cmd in dev_cmds:
+ try:
+ self.dbus_dev_setup()
+ except dbus.DBusException, e:
+ print 'Failed to setup device interface: %s' % e
+ sys.exit(1)
+ self.call_dev_dbus_func()
+ elif not self.listen:
+ print 'Unknown command: %s' % self.cmd
+ self.usage()
+ sys.exit(1)
+
+ if self.listen:
+ signal(SIGINT, self.signal_cb)
+ signal(SIGTERM, self.signal_cb)
+ self.main_loop = gobject.MainLoop()
+ self.main_loop.run()
+
+if __name__ == '__main__':
+ gobject.threads_init()
+ dbus.glib.init_threads()
+
+ tester = Tester(sys.argv)
+ tester.run()
diff --git a/test/attest.c b/test/attest.c
new file mode 100644
index 00000000..531c2157
--- /dev/null
+++ b/test/attest.c
@@ -0,0 +1,183 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2001-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/rfcomm.h>
+
+static int at_command(int fd, char *cmd, int to)
+{
+ fd_set rfds;
+ struct timeval timeout;
+ char buf[1024];
+ int sel, len, i, n;
+
+ len = write(fd, cmd, strlen(cmd));
+
+ for (i = 0; i < 100; i++) {
+
+ FD_ZERO(&rfds);
+ FD_SET(fd, &rfds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = to;
+
+ if ((sel = select(fd + 1, &rfds, NULL, NULL, &timeout)) > 0) {
+
+ if (FD_ISSET(fd, &rfds)) {
+ memset(buf, 0, sizeof(buf));
+ len = read(fd, buf, sizeof(buf));
+ for (n = 0; n < len; n++)
+ printf("%c", buf[n]);
+ if (strstr(buf, "\r\nOK") != NULL)
+ break;
+ if (strstr(buf, "\r\nERROR") != NULL)
+ break;
+ if (strstr(buf, "\r\nCONNECT") != NULL)
+ break;
+ }
+
+ }
+
+ }
+
+ return 0;
+}
+
+static int open_device(char *device)
+{
+ struct termios ti;
+ int fd;
+
+ fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open serial port: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ /* Switch tty to RAW mode */
+ cfmakeraw(&ti);
+ tcsetattr(fd, TCSANOW, &ti);
+
+ return fd;
+}
+
+static int open_socket(bdaddr_t *bdaddr, uint8_t channel)
+{
+ struct sockaddr_rc addr;
+ int sk;
+
+ sk = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ fprintf(stderr, "Can't create socket: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, BDADDR_ANY);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Can't bind socket: %s (%d)\n",
+ strerror(errno), errno);
+ close(sk);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, bdaddr);
+ addr.rc_channel = channel;
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ fprintf(stderr, "Can't connect: %s (%d)\n",
+ strerror(errno), errno);
+ close(sk);
+ return -1;
+ }
+
+ return sk;
+}
+
+static void usage(void)
+{
+ printf("Usage:\n\tattest <device> | <bdaddr> [channel]\n");
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+
+ bdaddr_t bdaddr;
+ uint8_t channel;
+
+ switch (argc) {
+ case 2:
+ str2ba(argv[1], &bdaddr);
+ channel = 1;
+ break;
+ case 3:
+ str2ba(argv[1], &bdaddr);
+ channel = atoi(argv[2]);
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+
+ if (bacmp(BDADDR_ANY, &bdaddr)) {
+ printf("Connecting to %s on channel %d\n", argv[1], channel);
+ fd = open_socket(&bdaddr, channel);
+ } else {
+ printf("Opening device %s\n", argv[1]);
+ fd = open_device(argv[1]);
+ }
+
+ if (fd < 0)
+ exit(-2);
+
+ at_command(fd, "ATZ\r\n", 10000);
+ at_command(fd, "AT+CPBS=\"ME\"\r\n", 10000);
+ at_command(fd, "AT+CPBR=1,100\r\n", 100000);
+
+ close(fd);
+
+ return 0;
+}
diff --git a/test/auth-agent.c b/test/auth-agent.c
new file mode 100644
index 00000000..2686c3ee
--- /dev/null
+++ b/test/auth-agent.c
@@ -0,0 +1,351 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#define INTERFACE "org.bluez.Security"
+
+static volatile sig_atomic_t __io_canceled = 0;
+static volatile sig_atomic_t __io_terminated = 0;
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static DBusHandlerResult agent_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *name, *old, *new;
+
+ if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!strcmp(name, "org.bluez") && *new == '\0') {
+ fprintf(stderr, "Authorization service has been terminated\n");
+ __io_terminated = 1;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult authorize_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *adapter, *address, *service, *string;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &adapter, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_STRING, &service, DBUS_TYPE_STRING, &string,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for passkey Confirm method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ printf("Authorization request for device %s\n", address);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ fprintf(stderr, "Can't create reply message\n");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult cancel_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *adapter, *address, *service, *string;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &adapter, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_STRING, &service, DBUS_TYPE_STRING, &string,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for passkey Confirm method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ printf("Request canceled for device %s\n", address);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ fprintf(stderr, "Can't create reply message\n");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult release_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for Release method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!__io_canceled)
+ fprintf(stderr, "Authorization agent has been released\n");
+
+ __io_terminated = 1;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ fprintf(stderr, "Can't create reply message\n");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult auth_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ if (dbus_message_is_method_call(msg, "org.bluez.AuthorizationAgent", "Authorize"))
+ return authorize_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.AuthorizationAgent", "Cancel"))
+ return cancel_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.AuthorizationAgent", "Release"))
+ return release_message(conn, msg, data);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable auth_table = {
+ .message_function = auth_message,
+};
+
+static int register_auth(DBusConnection *conn, const char *auth_path)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+
+ if (!dbus_connection_register_object_path(conn, auth_path,
+ &auth_table, NULL)) {
+ fprintf(stderr, "Can't register object path for agent\n");
+ return -1;
+ }
+
+ msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
+ INTERFACE, "RegisterDefaultAuthorizationAgent");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return -1;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &auth_path,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't register authorization agent\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return -1;
+ }
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return 0;
+}
+
+static int unregister_auth(DBusConnection *conn, const char *auth_path)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+
+ msg = dbus_message_new_method_call("org.bluez", "/org/bluez",
+ INTERFACE, "UnregisterDefaultAuthorizationAgent");
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ dbus_connection_unref(conn);
+ exit(1);
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &auth_path,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't unregister authorization agent\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return -1;
+ }
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ dbus_connection_unregister_object_path(conn, auth_path);
+
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("Bluetooth authorization agent ver %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\tauth-agent [--path auth-path]\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "path", 1, 0, 'p' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ DBusConnection *conn;
+ char match_string[128], default_path[128], *auth_path = NULL;
+ int opt;
+
+ snprintf(default_path, sizeof(default_path),
+ "/org/bluez/auth_agent_%d", getpid());
+
+ while ((opt = getopt_long(argc, argv, "+p:h", main_options, NULL)) != EOF) {
+ switch(opt) {
+ case 'p':
+ if (optarg[0] != '/') {
+ fprintf(stderr, "Invalid path\n");
+ exit(1);
+ }
+ auth_path = strdup(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (!auth_path)
+ auth_path = strdup(default_path);
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ fprintf(stderr, "Can't get on system bus");
+ exit(1);
+ }
+
+ if (register_auth(conn, auth_path) < 0) {
+ dbus_connection_unref(conn);
+ exit(1);
+ }
+
+ if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
+ fprintf(stderr, "Can't add signal filter");
+
+ snprintf(match_string, sizeof(match_string),
+ "interface=%s,member=NameOwnerChanged,arg0=%s",
+ DBUS_INTERFACE_DBUS, "org.bluez");
+
+ dbus_bus_add_match(conn, match_string, NULL);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ while (!__io_canceled && !__io_terminated) {
+ if (dbus_connection_read_write_dispatch(conn, 500) != TRUE)
+ break;
+ }
+
+ if (!__io_terminated)
+ unregister_auth(conn, auth_path);
+
+ dbus_connection_unref(conn);
+
+ return 0;
+}
diff --git a/test/bdaddr.8 b/test/bdaddr.8
new file mode 100644
index 00000000..78d47945
--- /dev/null
+++ b/test/bdaddr.8
@@ -0,0 +1,68 @@
+.TH BDADDR 8 "Sep 27 2005" BlueZ "Linux System Administration"
+.SH NAME
+bdaddr \- Utility for changing the Bluetooth device address
+.SH SYNOPSIS
+.B bdaddr
+.br
+.B bdaddr -h
+.br
+.B bdaddr [-i <dev>] [-r] [-t] [new bdaddr]
+
+.SH DESCRIPTION
+.LP
+.B
+bdaddr
+is used to query or set the local Bluetooth device address (BD_ADDR). If run
+with no arguments,
+.B
+bdaddr
+prints the chip manufacturer's name, and the current BD_ADDR. If the IEEE OUI
+index file "oui.txt" is installed on the system, the BD_ADDR owner will be
+displayed. If the optional [new bdaddr] argument is given, the device will be
+reprogrammed with that address. This can either be permanent or temporary, as
+specified by the -t flag. In both cases, the device must be reset before the
+new address will become active. This can be done with a 'soft' reset by
+specifying the -r flag, or a 'hard' reset by removing and replugging the
+device. A 'hard' reset will cause the address to revert to the current
+non-volatile value.
+.PP
+.B
+bdaddr
+uses manufacturer specific commands to set the address, and is therefore
+device specific. For this reason, not all devices are supported, and not all
+options are supported on all devices.
+Current supported manufacturers are:
+.B Ericsson, Cambridge Silicon Radio (CSR), Texas Instruments (TI), Zeevo
+and
+.B ST Microelectronics (ST)
+
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i\ <dev>
+Specify a particular device to operate on. If not specified, default is the
+first available device.
+.TP
+.BI -r
+Reset device and make new BD_ADDR active.
+.B
+CSR
+devices only.
+.TP
+.BI -t
+Temporary change. Do not write to non-volatile memory.
+.B
+CSR
+devices only.
+.SH FILES
+.TP
+.I
+/usr/share/misc/oui.txt
+IEEE Organizationally Unique Identifier master file.
+Manually update from: http://standards.ieee.org/regauth/oui/oui.txt
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/test/bdaddr.c b/test/bdaddr.c
new file mode 100644
index 00000000..7c0df31d
--- /dev/null
+++ b/test/bdaddr.c
@@ -0,0 +1,460 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "oui.h"
+
+static int transient = 0;
+
+static int generic_reset_device(int dd)
+{
+ bdaddr_t bdaddr;
+ int err;
+
+ err = hci_send_cmd(dd, 0x03, 0x0003, 0, NULL);
+ if (err < 0)
+ return err;
+
+ return hci_read_bd_addr(dd, &bdaddr, 10000);
+}
+
+#define OCF_ERICSSON_WRITE_BD_ADDR 0x000d
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) ericsson_write_bd_addr_cp;
+#define ERICSSON_WRITE_BD_ADDR_CP_SIZE 6
+
+static int ericsson_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+ struct hci_request rq;
+ ericsson_write_bd_addr_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_ERICSSON_WRITE_BD_ADDR;
+ rq.cparam = &cp;
+ rq.clen = ERICSSON_WRITE_BD_ADDR_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+
+#define OCF_ERICSSON_STORE_IN_FLASH 0x0022
+typedef struct {
+ uint8_t user_id;
+ uint8_t flash_length;
+ uint8_t flash_data[253];
+} __attribute__ ((packed)) ericsson_store_in_flash_cp;
+#define ERICSSON_STORE_IN_FLASH_CP_SIZE 255
+
+static int ericsson_store_in_flash(int dd, uint8_t user_id, uint8_t flash_length, uint8_t *flash_data)
+{
+ struct hci_request rq;
+ ericsson_store_in_flash_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.user_id = user_id;
+ cp.flash_length = flash_length;
+ if (flash_length > 0)
+ memcpy(cp.flash_data, flash_data, flash_length);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_ERICSSON_STORE_IN_FLASH;
+ rq.cparam = &cp;
+ rq.clen = ERICSSON_STORE_IN_FLASH_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int csr_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+ unsigned char cmd[] = { 0x02, 0x00, 0x0c, 0x00, 0x11, 0x47, 0x03, 0x70,
+ 0x00, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ if (transient)
+ cmd[14] = 0x08;
+
+ cmd[16] = bdaddr->b[2];
+ cmd[17] = 0x00;
+ cmd[18] = bdaddr->b[0];
+ cmd[19] = bdaddr->b[1];
+ cmd[20] = bdaddr->b[3];
+ cmd[21] = 0x00;
+ cmd[22] = bdaddr->b[4];
+ cmd[23] = bdaddr->b[5];
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x00;
+ rq.event = EVT_VENDOR;
+ rq.cparam = cp;
+ rq.clen = sizeof(cmd) + 1;
+ rq.rparam = rp;
+ rq.rlen = sizeof(rp);
+
+ if (hci_send_req(dd, &rq, 2000) < 0)
+ return -1;
+
+ if (rp[0] != 0xc2) {
+ errno = EIO;
+ return -1;
+ }
+
+ if ((rp[9] + (rp[10] << 8)) != 0) {
+ errno = ENXIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+static int csr_reset_device(int dd)
+{
+ unsigned char cmd[] = { 0x02, 0x00, 0x09, 0x00,
+ 0x00, 0x00, 0x01, 0x40, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+ unsigned char cp[254], rp[254];
+ struct hci_request rq;
+
+ if (transient)
+ cmd[6] = 0x02;
+
+ memset(&cp, 0, sizeof(cp));
+ cp[0] = 0xc2;
+ memcpy(cp + 1, cmd, sizeof(cmd));
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = 0x00;
+ rq.event = EVT_VENDOR;
+ rq.cparam = cp;
+ rq.clen = sizeof(cmd) + 1;
+ rq.rparam = rp;
+ rq.rlen = sizeof(rp);
+
+ if (hci_send_req(dd, &rq, 2000) < 0)
+ return -1;
+
+ return 0;
+}
+
+#define OCF_TI_WRITE_BD_ADDR 0x0006
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) ti_write_bd_addr_cp;
+#define TI_WRITE_BD_ADDR_CP_SIZE 6
+
+static int ti_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+ struct hci_request rq;
+ ti_write_bd_addr_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_TI_WRITE_BD_ADDR;
+ rq.cparam = &cp;
+ rq.clen = TI_WRITE_BD_ADDR_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+
+#define OCF_BCM_WRITE_BD_ADDR 0x0001
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) bcm_write_bd_addr_cp;
+#define BCM_WRITE_BD_ADDR_CP_SIZE 6
+
+static int bcm_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+ struct hci_request rq;
+ bcm_write_bd_addr_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_BCM_WRITE_BD_ADDR;
+ rq.cparam = &cp;
+ rq.clen = BCM_WRITE_BD_ADDR_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+
+#define OCF_ZEEVO_WRITE_BD_ADDR 0x0001
+typedef struct {
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) zeevo_write_bd_addr_cp;
+#define ZEEVO_WRITE_BD_ADDR_CP_SIZE 6
+
+static int zeevo_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+ struct hci_request rq;
+ zeevo_write_bd_addr_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_ZEEVO_WRITE_BD_ADDR;
+ rq.cparam = &cp;
+ rq.clen = ZEEVO_WRITE_BD_ADDR_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int st_write_bd_addr(int dd, bdaddr_t *bdaddr)
+{
+ return ericsson_store_in_flash(dd, 0xfe, 6, (uint8_t *) bdaddr);
+}
+
+static struct {
+ uint16_t compid;
+ int (*write_bd_addr)(int dd, bdaddr_t *bdaddr);
+ int (*reset_device)(int dd);
+} vendor[] = {
+ { 0, ericsson_write_bd_addr, NULL },
+ { 10, csr_write_bd_addr, csr_reset_device },
+ { 13, ti_write_bd_addr, NULL },
+ { 15, bcm_write_bd_addr, generic_reset_device },
+ { 18, zeevo_write_bd_addr, NULL },
+ { 48, st_write_bd_addr, generic_reset_device },
+ { 57, ericsson_write_bd_addr, generic_reset_device },
+ { 65535, NULL, NULL },
+};
+
+static void usage(void)
+{
+ printf("bdaddr - Utility for changing the Bluetooth device address\n\n");
+ printf("Usage:\n"
+ "\tbdaddr [-i <dev>] [-r] [-t] [new bdaddr]\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { "reset", 0, 0, 'r' },
+ { "transient", 0, 0, 't' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct hci_dev_info di;
+ struct hci_version ver;
+ bdaddr_t bdaddr;
+ char addr[18], oui[9], *comp;
+ int i, dd, opt, dev = 0, reset = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt_long(argc, argv, "+i:rth", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ dev = hci_devid(optarg);
+ if (dev < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ break;
+
+ case 'r':
+ reset = 1;
+ break;
+
+ case 't':
+ transient = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ dd = hci_open_dev(dev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_devinfo(dev, &di) < 0) {
+ fprintf(stderr, "Can't get device info for hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ exit(1);
+ }
+
+ if (hci_read_local_version(dd, &ver, 1000) < 0) {
+ fprintf(stderr, "Can't read version info for hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ exit(1);
+ }
+
+ if (!bacmp(&di.bdaddr, BDADDR_ANY)) {
+ if (hci_read_bd_addr(dd, &bdaddr, 1000) < 0) {
+ fprintf(stderr, "Can't read address for hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ exit(1);
+ }
+ } else
+ bacpy(&bdaddr, &di.bdaddr);
+
+ printf("Manufacturer: %s (%d)\n",
+ bt_compidtostr(ver.manufacturer), ver.manufacturer);
+
+ ba2oui(&bdaddr, oui);
+ comp = ouitocomp(oui);
+
+ ba2str(&bdaddr, addr);
+ printf("Device address: %s", addr);
+
+ if (comp) {
+ printf(" (%s)\n", comp);
+ free(comp);
+ } else
+ printf("\n");
+
+ if (argc < 1) {
+ hci_close_dev(dd);
+ exit(0);
+ }
+
+ str2ba(argv[0], &bdaddr);
+ if (!bacmp(&bdaddr, BDADDR_ANY)) {
+ hci_close_dev(dd);
+ exit(0);
+ }
+
+ for (i = 0; vendor[i].compid != 65535; i++)
+ if (ver.manufacturer == vendor[i].compid) {
+ ba2oui(&bdaddr, oui);
+ comp = ouitocomp(oui);
+
+ ba2str(&bdaddr, addr);
+ printf("New BD address: %s", addr);
+
+ if (comp) {
+ printf(" (%s)\n\n", comp);
+ free(comp);
+ } else
+ printf("\n\n");
+
+
+ if (vendor[i].write_bd_addr(dd, &bdaddr) < 0) {
+ fprintf(stderr, "Can't write new address\n");
+ hci_close_dev(dd);
+ exit(1);
+ }
+
+ printf("Address changed - ");
+
+ if (reset && vendor[i].reset_device) {
+ if (vendor[i].reset_device(dd) < 0) {
+ printf("Reset device manually\n");
+ } else {
+ ioctl(dd, HCIDEVRESET, dev);
+ printf("Device reset successully\n");
+ }
+ } else {
+ printf("Reset device now\n");
+ }
+
+ //ioctl(dd, HCIDEVRESET, dev);
+ //ioctl(dd, HCIDEVDOWN, dev);
+ //ioctl(dd, HCIDEVUP, dev);
+
+ hci_close_dev(dd);
+ exit(0);
+ }
+
+ hci_close_dev(dd);
+
+ printf("\n");
+ fprintf(stderr, "Unsupported manufacturer\n");
+
+ exit(1);
+}
diff --git a/test/dbusdef.py b/test/dbusdef.py
new file mode 100644
index 00000000..ca6debf4
--- /dev/null
+++ b/test/dbusdef.py
@@ -0,0 +1,59 @@
+import dbus
+
+bus = dbus.SystemBus()
+
+
+dummy = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.freedesktop.DBus.Introspectable')
+
+#print dummy.Introspect()
+
+
+manager = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Manager')
+
+database = dbus.Interface(bus.get_object('org.bluez', '/org/bluez'), 'org.bluez.Database')
+
+
+try:
+ adapter = dbus.Interface(bus.get_object('org.bluez', manager.DefaultAdapter()), 'org.bluez.Adapter')
+
+ test = dbus.Interface(bus.get_object('org.bluez', manager.DefaultAdapter()), 'org.bluez.Test')
+
+ rfcomm = dbus.Interface(bus.get_object('org.bluez', manager.DefaultAdapter()), 'org.bluez.RFCOMM')
+except:
+ adapter = ""
+
+ test = ""
+
+ rfcomm = ""
+
+
+def create_service(identifier):
+ try:
+ path = manager.FindService(identifier)
+ except:
+ path = ""
+
+ if (path != ""):
+ return dbus.Interface(bus.get_object('org.bluez', path), 'org.bluez.Service')
+
+echo = create_service("echo")
+
+transfer = create_service("transfer")
+
+network = create_service("network")
+
+input = create_service("input")
+
+audio = create_service("audio")
+
+headset = create_service("headset")
+
+
+def connect_service(identifier):
+ try:
+ conn = manager.ActivateService(identifier)
+ except:
+ conn = ""
+
+ if (conn != ""):
+ return dbus.Interface(bus.get_object(conn, "/org/bluez/" + identifier), 'org.bluez.' + identifier + '.Manager')
diff --git a/test/hciemu.c b/test/hciemu.c
new file mode 100644
index 00000000..bebcd9c8
--- /dev/null
+++ b/test/hciemu.c
@@ -0,0 +1,1346 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/resource.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <netdb.h>
+
+#include <glib.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+static inline uint64_t ntoh64(uint64_t n)
+{
+ uint64_t h;
+ uint64_t tmp = ntohl(n & 0x00000000ffffffff);
+ h = ntohl(n >> 32);
+ h |= tmp << 32;
+ return h;
+}
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define ntoh64(x) (x)
+#else
+#error "Unknown byte order"
+#endif
+#define hton64(x) ntoh64(x)
+
+#define GHCI_DEV "/dev/ghci"
+
+#define VHCI_DEV "/dev/vhci"
+#define VHCI_UDEV "/dev/hci_vhci"
+
+#define VHCI_MAX_CONN 12
+
+#define VHCI_ACL_MTU 192
+#define VHCI_ACL_MAX_PKT 8
+
+struct vhci_device {
+ uint8_t features[8];
+ uint8_t name[248];
+ uint8_t dev_class[3];
+ uint8_t inq_mode;
+ uint8_t eir_fec;
+ uint8_t eir_data[240];
+ uint16_t acl_cnt;
+ bdaddr_t bdaddr;
+ int fd;
+ int dd;
+ GIOChannel *scan;
+};
+
+struct vhci_conn {
+ bdaddr_t dest;
+ uint16_t handle;
+ GIOChannel *chan;
+};
+
+struct vhci_link_info {
+ bdaddr_t bdaddr;
+ uint8_t dev_class[3];
+ uint8_t link_type;
+ uint8_t role;
+} __attribute__ ((packed));
+
+static struct vhci_device vdev;
+static struct vhci_conn *vconn[VHCI_MAX_CONN];
+
+struct btsnoop_hdr {
+ uint8_t id[8]; /* Identification Pattern */
+ uint32_t version; /* Version Number = 1 */
+ uint32_t type; /* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+ uint32_t size; /* Original Length */
+ uint32_t len; /* Included Length */
+ uint32_t flags; /* Packet Flags */
+ uint32_t drops; /* Cumulative Drops */
+ uint64_t ts; /* Timestamp microseconds */
+ uint8_t data[0]; /* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
+
+static GMainLoop *event_loop;
+
+static volatile sig_atomic_t __io_canceled;
+
+static inline void io_init(void)
+{
+ __io_canceled = 0;
+}
+
+static inline void io_cancel(void)
+{
+ __io_canceled = 1;
+}
+
+static void sig_term(int sig)
+{
+ io_cancel();
+ g_main_loop_quit(event_loop);
+}
+
+static gboolean io_acl_data(GIOChannel *chan, GIOCondition cond, gpointer data);
+static gboolean io_conn_ind(GIOChannel *chan, GIOCondition cond, gpointer data);
+static gboolean io_hci_data(GIOChannel *chan, GIOCondition cond, gpointer data);
+
+static inline int read_n(int fd, void *buf, int len)
+{
+ register int w, t = 0;
+
+ while (!__io_canceled && len > 0) {
+ if ((w = read(fd, buf, len)) < 0 ){
+ if( errno == EINTR || errno == EAGAIN )
+ continue;
+ return -1;
+ }
+ if (!w)
+ return 0;
+ len -= w; buf += w; t += w;
+ }
+ return t;
+}
+
+/* Write exactly len bytes (Signal safe)*/
+static inline int write_n(int fd, void *buf, int len)
+{
+ register int w, t = 0;
+
+ while (!__io_canceled && len > 0) {
+ if ((w = write(fd, buf, len)) < 0 ){
+ if( errno == EINTR || errno == EAGAIN )
+ continue;
+ return -1;
+ }
+ if (!w)
+ return 0;
+ len -= w; buf += w; t += w;
+ }
+ return t;
+}
+
+static int create_snoop(char *file)
+{
+ struct btsnoop_hdr hdr;
+ int fd, len;
+
+ fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ return fd;
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htonl(1);
+ hdr.type = htonl(1002);
+
+ len = write(fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (len < 0) {
+ close(fd);
+ return -EIO;
+ }
+
+ if (len != BTSNOOP_HDR_SIZE) {
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+
+static int write_snoop(int fd, int type, int incoming, unsigned char *buf, int len)
+{
+ struct btsnoop_pkt pkt;
+ struct timeval tv;
+ uint32_t size = len;
+ uint64_t ts;
+ int err;
+
+ if (fd < 0)
+ return -1;
+
+ memset(&tv, 0, sizeof(tv));
+ gettimeofday(&tv, NULL);
+ ts = (tv.tv_sec - 946684800ll) * 1000000ll + tv.tv_usec;
+
+ pkt.size = htonl(size);
+ pkt.len = pkt.size;
+ pkt.flags = ntohl(incoming & 0x01);
+ pkt.drops = htonl(0);
+ pkt.ts = hton64(ts + 0x00E03AB44A676000ll);
+
+ if (type == HCI_COMMAND_PKT || type == HCI_EVENT_PKT)
+ pkt.flags |= ntohl(0x02);
+
+ err = write(fd, &pkt, BTSNOOP_PKT_SIZE);
+ err = write(fd, buf, size);
+
+ return 0;
+}
+
+static struct vhci_conn *conn_get_by_bdaddr(bdaddr_t *ba)
+{
+ register int i;
+
+ for (i = 0; i < VHCI_MAX_CONN; i++)
+ if (!bacmp(&vconn[i]->dest, ba))
+ return vconn[i];
+
+ return NULL;
+}
+
+static void command_status(uint16_t ogf, uint16_t ocf, uint8_t status)
+{
+ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+ evt_cmd_status *cs;
+ hci_event_hdr *he;
+
+ /* Packet type */
+ *ptr++ = HCI_EVENT_PKT;
+
+ /* Event header */
+ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+ he->evt = EVT_CMD_STATUS;
+ he->plen = EVT_CMD_STATUS_SIZE;
+
+ cs = (void *) ptr; ptr += EVT_CMD_STATUS_SIZE;
+
+ cs->status = status;
+ cs->ncmd = 1;
+ cs->opcode = htobs(cmd_opcode_pack(ogf, ocf));
+
+ write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+ if (write(vdev.fd, buf, ptr - buf) < 0)
+ syslog(LOG_ERR, "Can't send event: %s(%d)",
+ strerror(errno), errno);
+}
+
+static void command_complete(uint16_t ogf, uint16_t ocf, int plen, void *data)
+{
+ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+ evt_cmd_complete *cc;
+ hci_event_hdr *he;
+
+ /* Packet type */
+ *ptr++ = HCI_EVENT_PKT;
+
+ /* Event header */
+ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+ he->evt = EVT_CMD_COMPLETE;
+ he->plen = EVT_CMD_COMPLETE_SIZE + plen;
+
+ cc = (void *) ptr; ptr += EVT_CMD_COMPLETE_SIZE;
+
+ cc->ncmd = 1;
+ cc->opcode = htobs(cmd_opcode_pack(ogf, ocf));
+
+ if (plen) {
+ memcpy(ptr, data, plen);
+ ptr += plen;
+ }
+
+ write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+ if (write(vdev.fd, buf, ptr - buf) < 0)
+ syslog(LOG_ERR, "Can't send event: %s(%d)",
+ strerror(errno), errno);
+}
+
+static void connect_request(struct vhci_conn *conn)
+{
+ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+ evt_conn_request *cr;
+ hci_event_hdr *he;
+
+ /* Packet type */
+ *ptr++ = HCI_EVENT_PKT;
+
+ /* Event header */
+ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+ he->evt = EVT_CONN_REQUEST;
+ he->plen = EVT_CONN_REQUEST_SIZE;
+
+ cr = (void *) ptr; ptr += EVT_CONN_REQUEST_SIZE;
+
+ bacpy(&cr->bdaddr, &conn->dest);
+ memset(&cr->dev_class, 0, sizeof(cr->dev_class));
+ cr->link_type = ACL_LINK;
+
+ write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+ if (write(vdev.fd, buf, ptr - buf) < 0)
+ syslog(LOG_ERR, "Can't send event: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void connect_complete(struct vhci_conn *conn)
+{
+ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+ evt_conn_complete *cc;
+ hci_event_hdr *he;
+
+ /* Packet type */
+ *ptr++ = HCI_EVENT_PKT;
+
+ /* Event header */
+ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+ he->evt = EVT_CONN_COMPLETE;
+ he->plen = EVT_CONN_COMPLETE_SIZE;
+
+ cc = (void *) ptr; ptr += EVT_CONN_COMPLETE_SIZE;
+
+ bacpy(&cc->bdaddr, &conn->dest);
+ cc->status = 0x00;
+ cc->handle = htobs(conn->handle);
+ cc->link_type = ACL_LINK;
+ cc->encr_mode = 0x00;
+
+ write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+ if (write(vdev.fd, buf, ptr - buf) < 0)
+ syslog(LOG_ERR, "Can't send event: %s (%d)",
+ strerror(errno), errno);
+}
+
+static void disconn_complete(struct vhci_conn *conn)
+{
+ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+ evt_disconn_complete *dc;
+ hci_event_hdr *he;
+
+ /* Packet type */
+ *ptr++ = HCI_EVENT_PKT;
+
+ /* Event header */
+ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+ he->evt = EVT_DISCONN_COMPLETE;
+ he->plen = EVT_DISCONN_COMPLETE_SIZE;
+
+ dc = (void *) ptr; ptr += EVT_DISCONN_COMPLETE_SIZE;
+
+ dc->status = 0x00;
+ dc->handle = htobs(conn->handle);
+ dc->reason = 0x00;
+
+ write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+ if (write(vdev.fd, buf, ptr - buf) < 0)
+ syslog(LOG_ERR, "Can't send event: %s (%d)",
+ strerror(errno), errno);
+
+ vdev.acl_cnt = 0;
+}
+
+static void num_completed_pkts(struct vhci_conn *conn)
+{
+ uint8_t buf[HCI_MAX_FRAME_SIZE], *ptr = buf;
+ evt_num_comp_pkts *np;
+ hci_event_hdr *he;
+
+ /* Packet type */
+ *ptr++ = HCI_EVENT_PKT;
+
+ /* Event header */
+ he = (void *) ptr; ptr += HCI_EVENT_HDR_SIZE;
+
+ he->evt = EVT_NUM_COMP_PKTS;
+ he->plen = EVT_NUM_COMP_PKTS_SIZE;
+
+ np = (void *) ptr; ptr += EVT_NUM_COMP_PKTS_SIZE;
+ np->num_hndl = 1;
+
+ *((uint16_t *) ptr) = htobs(conn->handle); ptr += 2;
+ *((uint16_t *) ptr) = htobs(vdev.acl_cnt); ptr += 2;
+
+ write_snoop(vdev.dd, HCI_EVENT_PKT, 1, buf, ptr - buf);
+
+ if (write(vdev.fd, buf, ptr - buf) < 0)
+ syslog(LOG_ERR, "Can't send event: %s (%d)",
+ strerror(errno), errno);
+}
+
+static int scan_enable(uint8_t *data)
+{
+ struct sockaddr_in sa;
+ GIOChannel *sk_io;
+ bdaddr_t ba;
+ int sk, opt;
+
+ if (!(*data & SCAN_PAGE)) {
+ if (vdev.scan) {
+ g_io_channel_close(vdev.scan);
+ vdev.scan = NULL;
+ }
+ return 0;
+ }
+
+ if (vdev.scan)
+ return 0;
+
+ if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ return 1;
+ }
+
+ opt = 1;
+ setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ baswap(&ba, &vdev.bdaddr);
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = *(uint32_t *) &ba;
+ sa.sin_port = *(uint16_t *) &ba.b[4];
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto failed;
+ }
+
+ if (listen(sk, 10)) {
+ syslog(LOG_ERR, "Can't listen on socket: %s (%d)",
+ strerror(errno), errno);
+ goto failed;
+ }
+
+ sk_io = g_io_channel_unix_new(sk);
+ g_io_add_watch(sk_io, G_IO_IN | G_IO_NVAL, io_conn_ind, NULL);
+ vdev.scan = sk_io;
+ return 0;
+
+failed:
+ close(sk);
+ return 1;
+}
+
+static void accept_connection(uint8_t *data)
+{
+ accept_conn_req_cp *cp = (void *) data;
+ struct vhci_conn *conn;
+
+ if (!(conn = conn_get_by_bdaddr(&cp->bdaddr)))
+ return;
+
+ connect_complete(conn);
+
+ g_io_add_watch(conn->chan, G_IO_IN | G_IO_NVAL | G_IO_HUP,
+ io_acl_data, (gpointer) conn);
+}
+
+static void close_connection(struct vhci_conn *conn)
+{
+ syslog(LOG_INFO, "Closing connection %s handle %d",
+ batostr(&conn->dest), conn->handle);
+
+ g_io_channel_close(conn->chan);
+ g_io_channel_unref(conn->chan);
+
+ vconn[conn->handle - 1] = NULL;
+ disconn_complete(conn);
+ free(conn);
+}
+
+static void disconnect(uint8_t *data)
+{
+ disconnect_cp *cp = (void *) data;
+ struct vhci_conn *conn;
+ uint16_t handle;
+
+ handle = btohs(cp->handle);
+
+ if (handle - 1 > VHCI_MAX_CONN)
+ return;
+
+ if (!(conn = vconn[handle-1]))
+ return;
+
+ close_connection(conn);
+}
+
+static void create_connection(uint8_t *data)
+{
+ create_conn_cp *cp = (void *) data;
+ struct vhci_link_info info;
+ struct vhci_conn *conn;
+ struct sockaddr_in sa;
+ int h, sk, opt;
+ bdaddr_t ba;
+
+ for (h = 0; h < VHCI_MAX_CONN; h++)
+ if (!vconn[h])
+ goto do_connect;
+
+ syslog(LOG_ERR, "Too many connections");
+ return;
+
+do_connect:
+ if ((sk = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ return;
+ }
+
+ opt = 1;
+ setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
+
+ baswap(&ba, &vdev.bdaddr);
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = INADDR_ANY; // *(uint32_t *) &ba;
+ sa.sin_port = 0; // *(uint16_t *) &ba.b[4];
+ if (bind(sk, (struct sockaddr *) &sa, sizeof(sa))) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ close(sk);
+ return;
+ }
+
+ baswap(&ba, &cp->bdaddr);
+ sa.sin_family = AF_INET;
+ sa.sin_addr.s_addr = *(uint32_t *) &ba;
+ sa.sin_port = *(uint16_t *) &ba.b[4];
+ if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) {
+ syslog(LOG_ERR, "Can't connect: %s (%d)",
+ strerror(errno), errno);
+ close(sk);
+ return;
+ }
+
+ /* Send info */
+ memset(&info, 0, sizeof(info));
+ bacpy(&info.bdaddr, &vdev.bdaddr);
+ info.link_type = ACL_LINK;
+ info.role = 1;
+ write_n(sk, (void *) &info, sizeof(info));
+
+ if (!(conn = malloc(sizeof(*conn)))) {
+ syslog(LOG_ERR, "Can't alloc new connection: %s (%d)",
+ strerror(errno), errno);
+ close(sk);
+ return;
+ }
+
+ memcpy((uint8_t *) &ba, (uint8_t *) &sa.sin_addr, 4);
+ memcpy((uint8_t *) &ba.b[4], (uint8_t *) &sa.sin_port, 2);
+ baswap(&conn->dest, &ba);
+
+ vconn[h] = conn;
+ conn->handle = h + 1;
+ conn->chan = g_io_channel_unix_new(sk);
+
+ connect_complete(conn);
+ g_io_add_watch(conn->chan, G_IO_IN | G_IO_NVAL | G_IO_HUP,
+ io_acl_data, (gpointer) conn);
+ return;
+}
+
+static void inline hci_link_control(uint16_t ocf, int plen, uint8_t *data)
+{
+ uint8_t status;
+
+ const uint16_t ogf = OGF_LINK_CTL;
+
+ switch (ocf) {
+ case OCF_CREATE_CONN:
+ command_status(ogf, ocf, 0x00);
+ create_connection(data);
+ break;
+
+ case OCF_ACCEPT_CONN_REQ:
+ command_status(ogf, ocf, 0x00);
+ accept_connection(data);
+ break;
+
+ case OCF_DISCONNECT:
+ command_status(ogf, ocf, 0x00);
+ disconnect(data);
+ break;
+
+ default:
+ status = 0x01;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+ }
+}
+
+static void inline hci_link_policy(uint16_t ocf, int plen, uint8_t *data)
+{
+ uint8_t status;
+
+ const uint16_t ogf = OGF_INFO_PARAM;
+
+ switch (ocf) {
+ default:
+ status = 0x01;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+ }
+}
+
+static void inline hci_host_control(uint16_t ocf, int plen, uint8_t *data)
+{
+ read_local_name_rp ln;
+ read_class_of_dev_rp cd;
+ read_inquiry_mode_rp im;
+ read_ext_inquiry_response_rp ir;
+ uint8_t status;
+
+ const uint16_t ogf = OGF_HOST_CTL;
+
+ switch (ocf) {
+ case OCF_RESET:
+ status = 0x00;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_SET_EVENT_FLT:
+ status = 0x00;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_CHANGE_LOCAL_NAME:
+ status = 0x00;
+ memcpy(vdev.name, data, sizeof(vdev.name));
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_READ_LOCAL_NAME:
+ ln.status = 0x00;
+ memcpy(ln.name, vdev.name, sizeof(ln.name));
+ command_complete(ogf, ocf, sizeof(ln), &ln);
+ break;
+
+ case OCF_WRITE_CONN_ACCEPT_TIMEOUT:
+ case OCF_WRITE_PAGE_TIMEOUT:
+ status = 0x00;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_WRITE_SCAN_ENABLE:
+ status = scan_enable(data);
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_WRITE_AUTH_ENABLE:
+ status = 0x00;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_WRITE_ENCRYPT_MODE:
+ status = 0x00;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_READ_CLASS_OF_DEV:
+ cd.status = 0x00;
+ memcpy(cd.dev_class, vdev.dev_class, 3);
+ command_complete(ogf, ocf, sizeof(cd), &cd);
+ break;
+
+ case OCF_WRITE_CLASS_OF_DEV:
+ status = 0x00;
+ memcpy(vdev.dev_class, data, 3);
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_READ_INQUIRY_MODE:
+ im.status = 0x00;
+ im.mode = vdev.inq_mode;
+ command_complete(ogf, ocf, sizeof(im), &im);
+ break;
+
+ case OCF_WRITE_INQUIRY_MODE:
+ status = 0x00;
+ vdev.inq_mode = data[0];
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ case OCF_READ_EXT_INQUIRY_RESPONSE:
+ ir.status = 0x00;
+ ir.fec = vdev.eir_fec;
+ memcpy(ir.data, vdev.eir_data, 240);
+ command_complete(ogf, ocf, sizeof(ir), &ir);
+ break;
+
+ case OCF_WRITE_EXT_INQUIRY_RESPONSE:
+ status = 0x00;
+ vdev.eir_fec = data[0];
+ memcpy(vdev.eir_data, data + 1, 240);
+ command_complete(ogf, ocf, 1, &status);
+ break;
+
+ default:
+ status = 0x01;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+ }
+}
+
+static void inline hci_info_param(uint16_t ocf, int plen, uint8_t *data)
+{
+ read_local_version_rp lv;
+ read_local_features_rp lf;
+ read_local_ext_features_rp ef;
+ read_buffer_size_rp bs;
+ read_bd_addr_rp ba;
+ uint8_t status;
+
+ const uint16_t ogf = OGF_INFO_PARAM;
+
+ switch (ocf) {
+ case OCF_READ_LOCAL_VERSION:
+ lv.status = 0x00;
+ lv.hci_ver = 0x03;
+ lv.hci_rev = htobs(0x0000);
+ lv.lmp_ver = 0x03;
+ lv.manufacturer = htobs(29);
+ lv.lmp_subver = htobs(0x0000);
+ command_complete(ogf, ocf, sizeof(lv), &lv);
+ break;
+
+ case OCF_READ_LOCAL_FEATURES:
+ lf.status = 0x00;
+ memcpy(lf.features, vdev.features, 8);
+ command_complete(ogf, ocf, sizeof(lf), &lf);
+ break;
+
+ case OCF_READ_LOCAL_EXT_FEATURES:
+ ef.status = 0x00;
+ if (*data == 0) {
+ ef.page_num = 0;
+ ef.max_page_num = 0;
+ memcpy(ef.features, vdev.features, 8);
+ } else {
+ ef.page_num = *data;
+ ef.max_page_num = 0;
+ memset(ef.features, 0, 8);
+ }
+ command_complete(ogf, ocf, sizeof(ef), &ef);
+ break;
+
+ case OCF_READ_BUFFER_SIZE:
+ bs.status = 0x00;
+ bs.acl_mtu = htobs(VHCI_ACL_MTU);
+ bs.sco_mtu = 0;
+ bs.acl_max_pkt = htobs(VHCI_ACL_MAX_PKT);
+ bs.sco_max_pkt = htobs(0);
+ command_complete(ogf, ocf, sizeof(bs), &bs);
+ break;
+
+ case OCF_READ_BD_ADDR:
+ ba.status = 0x00;
+ bacpy(&ba.bdaddr, &vdev.bdaddr);
+ command_complete(ogf, ocf, sizeof(ba), &ba);
+ break;
+
+ default:
+ status = 0x01;
+ command_complete(ogf, ocf, 1, &status);
+ break;
+ }
+}
+
+static void hci_command(uint8_t *data)
+{
+ hci_command_hdr *ch;
+ uint8_t *ptr = data;
+ uint16_t ogf, ocf;
+
+ ch = (hci_command_hdr *) ptr;
+ ptr += HCI_COMMAND_HDR_SIZE;
+
+ ch->opcode = btohs(ch->opcode);
+ ogf = cmd_opcode_ogf(ch->opcode);
+ ocf = cmd_opcode_ocf(ch->opcode);
+
+ switch (ogf) {
+ case OGF_LINK_CTL:
+ hci_link_control(ocf, ch->plen, ptr);
+ break;
+
+ case OGF_LINK_POLICY:
+ hci_link_policy(ocf, ch->plen, ptr);
+ break;
+
+ case OGF_HOST_CTL:
+ hci_host_control(ocf, ch->plen, ptr);
+ break;
+
+ case OGF_INFO_PARAM:
+ hci_info_param(ocf, ch->plen, ptr);
+ break;
+ }
+}
+
+static void hci_acl_data(uint8_t *data)
+{
+ hci_acl_hdr *ah = (void *) data;
+ struct vhci_conn *conn;
+ uint16_t handle;
+ int fd;
+
+ handle = acl_handle(btohs(ah->handle));
+
+ if (handle > VHCI_MAX_CONN || !(conn = vconn[handle - 1])) {
+ syslog(LOG_ERR, "Bad connection handle %d", handle);
+ return;
+ }
+
+ fd = g_io_channel_unix_get_fd(conn->chan);
+ if (write_n(fd, data, btohs(ah->dlen) + HCI_ACL_HDR_SIZE) < 0) {
+ close_connection(conn);
+ return;
+ }
+
+ if (++vdev.acl_cnt > VHCI_ACL_MAX_PKT - 1) {
+ /* Send num of complete packets event */
+ num_completed_pkts(conn);
+ vdev.acl_cnt = 0;
+ }
+}
+
+static gboolean io_acl_data(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct vhci_conn *conn = (struct vhci_conn *) data;
+ unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+ hci_acl_hdr *ah;
+ uint16_t flags;
+ int fd, err, len;
+
+ if (cond & G_IO_NVAL) {
+ g_io_channel_unref(chan);
+ return FALSE;
+ }
+
+ if (cond & G_IO_HUP) {
+ close_connection(conn);
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(chan);
+
+ ptr = buf + 1;
+ if (read_n(fd, ptr, HCI_ACL_HDR_SIZE) <= 0) {
+ close_connection(conn);
+ return FALSE;
+ }
+
+ ah = (void *) ptr;
+ ptr += HCI_ACL_HDR_SIZE;
+
+ len = btohs(ah->dlen);
+ if (read_n(fd, ptr, len) <= 0) {
+ close_connection(conn);
+ return FALSE;
+ }
+
+ buf[0] = HCI_ACLDATA_PKT;
+
+ flags = acl_flags(btohs(ah->handle));
+ ah->handle = htobs(acl_handle_pack(conn->handle, flags));
+ len += HCI_ACL_HDR_SIZE + 1;
+
+ write_snoop(vdev.dd, HCI_ACLDATA_PKT, 1, buf, len);
+
+ err = write(vdev.fd, buf, len);
+
+ return TRUE;
+}
+
+static gboolean io_conn_ind(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ struct vhci_link_info info;
+ struct vhci_conn *conn;
+ struct sockaddr_in sa;
+ socklen_t len;
+ int sk, nsk, h;
+
+ if (cond & G_IO_NVAL)
+ return FALSE;
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ len = sizeof(sa);
+ if ((nsk = accept(sk, (struct sockaddr *) &sa, &len)) < 0)
+ return TRUE;
+
+ if (read_n(nsk, &info, sizeof(info)) < 0) {
+ syslog(LOG_ERR, "Can't read link info");
+ return TRUE;
+ }
+
+ if (!(conn = malloc(sizeof(*conn)))) {
+ syslog(LOG_ERR, "Can't alloc new connection");
+ close(nsk);
+ return TRUE;
+ }
+
+ bacpy(&conn->dest, &info.bdaddr);
+
+ for (h = 0; h < VHCI_MAX_CONN; h++)
+ if (!vconn[h])
+ goto accepted;
+
+ syslog(LOG_ERR, "Too many connections");
+ free(conn);
+ close(nsk);
+ return TRUE;
+
+accepted:
+ vconn[h] = conn;
+ conn->handle = h + 1;
+ conn->chan = g_io_channel_unix_new(nsk);
+ connect_request(conn);
+
+ return TRUE;
+}
+
+static gboolean io_hci_data(GIOChannel *chan, GIOCondition cond, gpointer data)
+{
+ unsigned char buf[HCI_MAX_FRAME_SIZE], *ptr;
+ int type;
+ gsize len;
+ GIOError err;
+
+ ptr = buf;
+
+ if ((err = g_io_channel_read(chan, (gchar *) buf, sizeof(buf), &len))) {
+ if (err == G_IO_ERROR_AGAIN)
+ return TRUE;
+
+ syslog(LOG_ERR, "Read failed: %s (%d)", strerror(errno), errno);
+ g_io_channel_unref(chan);
+ g_main_loop_quit(event_loop);
+ return FALSE;
+ }
+
+ type = *ptr++;
+
+ write_snoop(vdev.dd, type, 0, buf, len);
+
+ switch (type) {
+ case HCI_COMMAND_PKT:
+ hci_command(ptr);
+ break;
+
+ case HCI_ACLDATA_PKT:
+ hci_acl_data(ptr);
+ break;
+
+ default:
+ syslog(LOG_ERR, "Unknown packet type 0x%2.2x", type);
+ break;
+ }
+
+ return TRUE;
+}
+
+static int getbdaddrbyname(char *str, bdaddr_t *ba)
+{
+ int i, n, len;
+
+ len = strlen(str);
+
+ /* Check address format */
+ for (i = 0, n = 0; i < len; i++)
+ if (str[i] == ':')
+ n++;
+
+ if (n == 5) {
+ /* BD address */
+ baswap(ba, strtoba(str));
+ return 0;
+ }
+
+ if (n == 1) {
+ /* IP address + port */
+ struct hostent *hent;
+ bdaddr_t b;
+ char *ptr;
+
+ ptr = strchr(str, ':');
+ *ptr++ = 0;
+
+ if (!(hent = gethostbyname(str))) {
+ fprintf(stderr, "Can't resolve %s\n", str);
+ return -2;
+ }
+
+ memcpy(&b, hent->h_addr, 4);
+ *(uint16_t *) (&b.b[4]) = htons(atoi(ptr));
+ baswap(ba, &b);
+
+ return 0;
+ }
+
+ fprintf(stderr, "Invalid address format\n");
+
+ return -1;
+}
+
+static void rewrite_bdaddr(unsigned char *buf, int len, bdaddr_t *bdaddr)
+{
+ hci_event_hdr *eh;
+ unsigned char *ptr = buf;
+ int type;
+
+ if (!bdaddr)
+ return;
+
+ if (!bacmp(bdaddr, BDADDR_ANY))
+ return;
+
+ type = *ptr++;
+
+ switch (type) {
+ case HCI_EVENT_PKT:
+ eh = (hci_event_hdr *) ptr;
+ ptr += HCI_EVENT_HDR_SIZE;
+
+ if (eh->evt == EVT_CMD_COMPLETE) {
+ evt_cmd_complete *cc = (void *) ptr;
+
+ ptr += EVT_CMD_COMPLETE_SIZE;
+
+ if (cc->opcode == htobs(cmd_opcode_pack(OGF_INFO_PARAM,
+ OCF_READ_BD_ADDR))) {
+ bacpy((bdaddr_t *) (ptr + 1), bdaddr);
+ }
+ }
+ break;
+ }
+}
+
+static int run_proxy(int fd, int dev, bdaddr_t *bdaddr)
+{
+ unsigned char buf[HCI_MAX_FRAME_SIZE + 1];
+ struct hci_dev_info di;
+ struct hci_filter flt;
+ struct pollfd p[2];
+ int dd, err, len, need_raw;
+
+ dd = hci_open_dev(dev);
+ if (dd < 0) {
+ syslog(LOG_ERR, "Can't open device hci%d: %s (%d)",
+ dev, strerror(errno), errno);
+ return 1;
+ }
+
+ if (hci_devinfo(dev, &di) < 0) {
+ syslog(LOG_ERR, "Can't get device info for hci%d: %s (%d)",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ return 1;
+ }
+
+ need_raw = !hci_test_bit(HCI_RAW, &di.flags);
+
+ hci_filter_clear(&flt);
+ hci_filter_all_ptypes(&flt);
+ hci_filter_all_events(&flt);
+
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ syslog(LOG_ERR, "Can't set filter for hci%d: %s (%d)",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ return 1;
+ }
+
+ if (need_raw) {
+ if (ioctl(dd, HCISETRAW, 1) < 0) {
+ syslog(LOG_ERR, "Can't set raw mode on hci%d: %s (%d)",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ return 1;
+ }
+ }
+
+ p[0].fd = fd;
+ p[0].events = POLLIN;
+ p[1].fd = dd;
+ p[1].events = POLLIN;
+
+ while (!__io_canceled) {
+ p[0].revents = 0;
+ p[1].revents = 0;
+ err = poll(p, 2, 500);
+ if (err < 0)
+ break;
+ if (!err)
+ continue;
+
+ if (p[0].revents & POLLIN) {
+ len = read(fd, buf, sizeof(buf));
+ if (len > 0) {
+ rewrite_bdaddr(buf, len, bdaddr);
+ err = write(dd, buf, len);
+ }
+ }
+
+ if (p[1].revents & POLLIN) {
+ len = read(dd, buf, sizeof(buf));
+ if (len > 0) {
+ rewrite_bdaddr(buf, len, bdaddr);
+ err = write(fd, buf, len);
+ }
+ }
+ }
+
+ if (need_raw) {
+ if (ioctl(dd, HCISETRAW, 0) < 0)
+ syslog(LOG_ERR, "Can't clear raw mode on hci%d: %s (%d)",
+ dev, strerror(errno), errno);
+ }
+
+ hci_close_dev(dd);
+
+ syslog(LOG_INFO, "Exit");
+
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("hciemu - HCI emulator ver %s\n", VERSION);
+ printf("Usage: \n");
+ printf("\thciemu [-n] local_address\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'd' },
+ { "bdaddr", 1, 0, 'b' },
+ { "snoop", 1, 0, 's' },
+ { "nodetach", 0, 0, 'n' },
+ { "help", 0, 0, 'h' },
+ { 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ GIOChannel *dev_io;
+ char *device = NULL, *snoop = NULL;
+ bdaddr_t bdaddr;
+ int fd, dd, opt, detach = 1, dev = -1;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt_long(argc, argv, "d:b:s:nh", main_options, NULL)) != EOF) {
+ switch(opt) {
+ case 'd':
+ device = strdup(optarg);
+ break;
+
+ case 'b':
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 's':
+ snoop = strdup(optarg);
+ break;
+
+ case 'n':
+ detach = 0;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ if (strlen(argv[0]) > 3 && !strncasecmp(argv[0], "hci", 3)) {
+ dev = hci_devid(argv[0]);
+ if (dev < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ } else {
+ if (getbdaddrbyname(argv[0], &vdev.bdaddr) < 0)
+ exit(1);
+ }
+
+ if (detach) {
+ if (daemon(0, 0)) {
+ perror("Can't start daemon");
+ exit(1);
+ }
+ }
+
+ /* Start logging to syslog and stderr */
+ openlog("hciemu", LOG_PID | LOG_NDELAY | LOG_PERROR, LOG_DAEMON);
+ syslog(LOG_INFO, "HCI emulation daemon ver %s started", VERSION);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ io_init();
+
+ if (!device && dev >= 0)
+ device = strdup(GHCI_DEV);
+
+ /* Open and create virtual HCI device */
+ if (device) {
+ fd = open(device, O_RDWR);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Can't open device %s: %s (%d)",
+ device, strerror(errno), errno);
+ free(device);
+ exit(1);
+ }
+ free(device);
+ } else {
+ fd = open(VHCI_DEV, O_RDWR);
+ if (fd < 0) {
+ fd = open(VHCI_UDEV, O_RDWR);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Can't open device %s: %s (%d)",
+ VHCI_DEV, strerror(errno), errno);
+ exit(1);
+ }
+ }
+ }
+
+ /* Create snoop file */
+ if (snoop) {
+ dd = create_snoop(snoop);
+ if (dd < 0)
+ syslog(LOG_ERR, "Can't create snoop file %s: %s (%d)",
+ snoop, strerror(errno), errno);
+ free(snoop);
+ } else
+ dd = -1;
+
+ /* Create event loop */
+ event_loop = g_main_loop_new(NULL, FALSE);
+
+ if (dev >= 0)
+ return run_proxy(fd, dev, &bdaddr);
+
+ /* Device settings */
+ vdev.features[0] = 0xff;
+ vdev.features[1] = 0xff;
+ vdev.features[2] = 0x8f;
+ vdev.features[3] = 0xfe;
+ vdev.features[4] = 0x9b;
+ vdev.features[5] = 0xf9;
+ vdev.features[6] = 0x01;
+ vdev.features[7] = 0x80;
+
+ memset(vdev.name, 0, sizeof(vdev.name));
+ strncpy((char *) vdev.name, "BlueZ (Virtual HCI)", sizeof(vdev.name));
+
+ vdev.dev_class[0] = 0x00;
+ vdev.dev_class[1] = 0x00;
+ vdev.dev_class[2] = 0x00;
+
+ vdev.inq_mode = 0x00;
+ vdev.eir_fec = 0x00;
+ memset(vdev.eir_data, 0, sizeof(vdev.eir_data));
+
+ vdev.fd = fd;
+ vdev.dd = dd;
+
+ dev_io = g_io_channel_unix_new(fd);
+ g_io_add_watch(dev_io, G_IO_IN, io_hci_data, NULL);
+
+ setpriority(PRIO_PROCESS, 0, -19);
+
+ /* Start event processor */
+ g_main_loop_run(event_loop);
+
+ close(fd);
+
+ if (dd >= 0)
+ close(dd);
+
+ syslog(LOG_INFO, "Exit");
+
+ return 0;
+}
diff --git a/test/hsmicro b/test/hsmicro
new file mode 100755
index 00000000..c254226b
--- /dev/null
+++ b/test/hsmicro
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+SOX=`which sox`
+HSTEST=`which hstest`
+
+if [ -z "$HSTEST" ]
+then
+ HSTEST="./hstest"
+fi
+
+if [ -z "$1" ]
+then
+ echo -e "Usage:\n\thsmicro <bdaddr> [channel]"
+ exit
+fi
+
+BDADDR=$1
+CHANNEL=$2
+
+$HSTEST record - $BDADDR $CHANNEL | $SOX -t raw -r 8000 -c 1 -s -w - -t ossdsp -r 44100 -c 2 -s -w /dev/dsp polyphase vol 5.0 2> /dev/null
diff --git a/test/hsplay b/test/hsplay
new file mode 100755
index 00000000..8cecbffa
--- /dev/null
+++ b/test/hsplay
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+MPG123=`which mpg123`
+SOX=`which sox`
+HSTEST=`which hstest`
+
+if [ -z "$HSTEST" ]
+then
+ HSTEST="./hstest"
+fi
+
+if [ -z "$1" ] || [ -z "$2" ]
+then
+ echo -e "Usage:\n\thsplay <file> <bdaddr> [channel]"
+ exit
+fi
+
+FILE=$1
+BDADDR=$2
+CHANNEL=$3
+
+$MPG123 -q -s "$FILE" | $SOX -t raw -r 44100 -c 2 -s -w - -t raw -r 8000 -c 1 -s -w - | $HSTEST play - $BDADDR $CHANNEL
diff --git a/test/hstest.c b/test/hstest.c
new file mode 100644
index 00000000..158bf204
--- /dev/null
+++ b/test/hstest.c
@@ -0,0 +1,308 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <termios.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sco.h>
+#include <bluetooth/rfcomm.h>
+
+static volatile int terminate = 0;
+
+static void sig_term(int sig) {
+ terminate = 1;
+}
+
+static int rfcomm_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t channel)
+{
+ struct sockaddr_rc addr;
+ int s;
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM)) < 0) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, src);
+ addr.rc_channel = 0;
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, dst);
+ addr.rc_channel = channel;
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
+ close(s);
+ return -1;
+ }
+
+ return s;
+}
+
+static int sco_connect(bdaddr_t *src, bdaddr_t *dst, uint16_t *handle, uint16_t *mtu)
+{
+ struct sockaddr_sco addr;
+ struct sco_conninfo conn;
+ struct sco_options opts;
+ socklen_t size;
+ int s;
+
+ if ((s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO)) < 0) {
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, src);
+
+ if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, dst);
+
+ if (connect(s, (struct sockaddr *)&addr, sizeof(addr)) < 0 ){
+ close(s);
+ return -1;
+ }
+
+ memset(&conn, 0, sizeof(conn));
+ size = sizeof(conn);
+
+ if (getsockopt(s, SOL_SCO, SCO_CONNINFO, &conn, &size) < 0) {
+ close(s);
+ return -1;
+ }
+
+ memset(&opts, 0, sizeof(opts));
+ size = sizeof(opts);
+
+ if (getsockopt(s, SOL_SCO, SCO_OPTIONS, &opts, &size) < 0) {
+ close(s);
+ return -1;
+ }
+
+ if (handle)
+ *handle = conn.hci_handle;
+
+ if (mtu)
+ *mtu = opts.mtu;
+
+ return s;
+}
+
+static void usage(void)
+{
+ printf("Usage:\n"
+ "\thstest play <file> <bdaddr> [channel]\n"
+ "\thstest record <file> <bdaddr> [channel]\n");
+}
+
+#define PLAY 1
+#define RECORD 2
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+
+ fd_set rfds;
+ struct timeval timeout;
+ unsigned char buf[2048], *p;
+ int maxfd, sel, rlen, wlen;
+
+ bdaddr_t local;
+ bdaddr_t bdaddr;
+ uint8_t channel;
+
+ char *filename;
+ mode_t filemode;
+ int err, mode = 0;
+ int dd, rd, sd, fd;
+ uint16_t sco_handle, sco_mtu, vs;
+
+ switch (argc) {
+ case 4:
+ str2ba(argv[3], &bdaddr);
+ channel = 6;
+ break;
+ case 5:
+ str2ba(argv[3], &bdaddr);
+ channel = atoi(argv[4]);
+ break;
+ default:
+ usage();
+ exit(-1);
+ }
+
+ if (strncmp(argv[1], "play", 4) == 0) {
+ mode = PLAY;
+ filemode = O_RDONLY;
+ } else if (strncmp(argv[1], "rec", 3) == 0) {
+ mode = RECORD;
+ filemode = O_WRONLY | O_CREAT | O_TRUNC;
+ } else {
+ usage();
+ exit(-1);
+ }
+
+ filename = argv[2];
+
+ hci_devba(0, &local);
+ dd = hci_open_dev(0);
+ hci_read_voice_setting(dd, &vs, 1000);
+ vs = htobs(vs);
+ fprintf(stderr, "Voice setting: 0x%04x\n", vs);
+ close(dd);
+ if (vs != 0x0060) {
+ fprintf(stderr, "The voice setting must be 0x0060\n");
+ return -1;
+ }
+
+ if (strcmp(filename, "-") == 0) {
+ switch (mode) {
+ case PLAY:
+ fd = 0;
+ break;
+ case RECORD:
+ fd = 1;
+ break;
+ default:
+ return -1;
+ }
+ } else {
+ if ((fd = open(filename, filemode)) < 0) {
+ perror("Can't open input/output file");
+ return -1;
+ }
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGCHLD, &sa, NULL);
+ sigaction(SIGPIPE, &sa, NULL);
+
+ if ((rd = rfcomm_connect(&local, &bdaddr, channel)) < 0) {
+ perror("Can't connect RFCOMM channel");
+ return -1;
+ }
+
+ fprintf(stderr, "RFCOMM channel connected\n");
+
+ if ((sd = sco_connect(&local, &bdaddr, &sco_handle, &sco_mtu)) < 0) {
+ perror("Can't connect SCO audio channel");
+ close(rd);
+ return -1;
+ }
+
+ fprintf(stderr, "SCO audio channel connected (handle %d, mtu %d)\n", sco_handle, sco_mtu);
+
+ if (mode == RECORD)
+ err = write(rd, "RING\r\n", 6);
+
+ maxfd = (rd > sd) ? rd : sd;
+
+ while (!terminate) {
+
+ FD_ZERO(&rfds);
+ FD_SET(rd, &rfds);
+ FD_SET(sd, &rfds);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 10000;
+
+ if ((sel = select(maxfd + 1, &rfds, NULL, NULL, &timeout)) > 0) {
+
+ if (FD_ISSET(rd, &rfds)) {
+ memset(buf, 0, sizeof(buf));
+ rlen = read(rd, buf, sizeof(buf));
+ if (rlen > 0) {
+ fprintf(stderr, "%s\n", buf);
+ wlen = write(rd, "OK\r\n", 4);
+ }
+ }
+
+ if (FD_ISSET(sd, &rfds)) {
+ memset(buf, 0, sizeof(buf));
+ rlen = read(sd, buf, sizeof(buf));
+ if (rlen > 0)
+ switch (mode) {
+ case PLAY:
+ rlen = read(fd, buf, rlen);
+
+ wlen = 0;
+ p = buf;
+ while (rlen > sco_mtu) {
+ wlen += write(sd, p, sco_mtu);
+ rlen -= sco_mtu;
+ p += sco_mtu;
+ }
+ wlen += write(sd, p, rlen);
+ break;
+ case RECORD:
+ wlen = write(fd, buf, rlen);
+ break;
+ default:
+ break;
+ }
+ }
+
+ }
+
+ }
+
+ close(sd);
+ sleep(5);
+ close(rd);
+
+ close(fd);
+
+ return 0;
+}
diff --git a/test/l2test.c b/test/l2test.c
new file mode 100644
index 00000000..bc57d6b6
--- /dev/null
+++ b/test/l2test.c
@@ -0,0 +1,1104 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/l2cap.h>
+
+#define NIBBLE_TO_ASCII(c) ((c) < 0x0a ? (c) + 0x30 : (c) + 0x57)
+
+/* Test modes */
+enum {
+ SEND,
+ RECV,
+ RECONNECT,
+ MULTY,
+ DUMP,
+ CONNECT,
+ CRECV,
+ LSEND,
+ SENDDUMP,
+ LSENDDUMP,
+ INFOREQ
+};
+
+static unsigned char *buf;
+
+/* Default mtu */
+static int imtu = 672;
+static int omtu = 0;
+
+/* Default data size */
+static long data_size = -1;
+
+/* Default addr and psm */
+static bdaddr_t bdaddr;
+static unsigned short psm = 10;
+
+/* Default number of frames to send (-1 = infinite) */
+static int num_frames = -1;
+
+/* Default number of consecutive frames before the delay */
+static int count = 1;
+
+/* Default delay after sending count number of frames */
+static unsigned long delay = 0;
+
+static char *filename = NULL;
+
+static int rfcmode = 0;
+static int master = 0;
+static int auth = 0;
+static int encrypt = 0;
+static int secure = 0;
+static int socktype = SOCK_SEQPACKET;
+static int linger = 0;
+static int reliable = 0;
+static int timestamp = 0;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static char *ltoh(unsigned long c, char* s)
+{
+ int c1;
+
+ c1 = (c >> 28) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = (c >> 24) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = (c >> 20) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = (c >> 16) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = (c >> 12) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = (c >> 8) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = (c >> 4) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = c & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ *s = 0;
+ return s;
+}
+
+static char *ctoh(char c, char* s)
+{
+ char c1;
+
+ c1 = (c >> 4) & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ c1 = c & 0x0f;
+ *(s++) = NIBBLE_TO_ASCII (c1);
+ *s = 0;
+ return s;
+}
+
+static void hexdump(unsigned char *s, unsigned long l)
+{
+ char bfr[80];
+ char *pb;
+ unsigned long i, n = 0;
+
+ if (l == 0)
+ return;
+
+ while (n < l) {
+ pb = bfr;
+ pb = ltoh (n, pb);
+ *(pb++) = ':';
+ *(pb++) = ' ';
+ for (i = 0; i < 16; i++) {
+ if (n + i >= l) {
+ *(pb++) = ' ';
+ *(pb++) = ' ';
+ } else
+ pb = ctoh (*(s + i), pb);
+ *(pb++) = ' ';
+ }
+ *(pb++) = ' ';
+ for (i = 0; i < 16; i++) {
+ if (n + i >= l)
+ break;
+ else
+ *(pb++) = (isprint (*(s + i)) ? *(s + i) : '.');
+ }
+ *pb = 0;
+ n += 16;
+ s += 16;
+ puts(bfr);
+ }
+}
+
+static int do_connect(char *svr)
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ struct l2cap_conninfo conn;
+ socklen_t optlen;
+ int sk, opt;
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Bind to local address */
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, &bdaddr);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Get default options */
+ memset(&opts, 0, sizeof(opts));
+ optlen = sizeof(opts);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Set new options */
+ opts.omtu = omtu;
+ opts.imtu = imtu;
+ if (rfcmode > 0)
+ opts.mode = rfcmode;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+ syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+#if 0
+ /* Enable SO_TIMESTAMP */
+ if (timestamp) {
+ int t = 1;
+
+ if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+#endif
+
+ /* Enable SO_LINGER */
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+
+ /* Set link mode */
+ opt = 0;
+ if (reliable)
+ opt |= L2CAP_LM_RELIABLE;
+ if (master)
+ opt |= L2CAP_LM_MASTER;
+ if (auth)
+ opt |= L2CAP_LM_AUTH;
+ if (encrypt)
+ opt |= L2CAP_LM_ENCRYPT;
+ if (secure)
+ opt |= L2CAP_LM_SECURE;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Connect to remote device */
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(svr, &addr.l2_bdaddr);
+ addr.l2_psm = htobs(psm);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+ syslog(LOG_ERR, "Can't connect: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Get current options */
+ memset(&opts, 0, sizeof(opts));
+ optlen = sizeof(opts);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Get connection information */
+ memset(&conn, 0, sizeof(conn));
+ optlen = sizeof(conn);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ syslog(LOG_INFO, "Connected [imtu %d, omtu %d, flush_to %d, "
+ "mode %d, handle %d, class 0x%02x%02x%02x]",
+ opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+ if (data_size > opts.omtu)
+ data_size = opts.omtu;
+
+ return sk;
+
+error:
+ close(sk);
+ return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+ struct sockaddr_l2 addr;
+ struct l2cap_options opts;
+ struct l2cap_conninfo conn;
+ socklen_t optlen;
+ int sk, nsk, opt;
+ char ba[18];
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, socktype, BTPROTO_L2CAP);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Bind to local address */
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, &bdaddr);
+ addr.l2_psm = htobs(psm);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Set link mode */
+ opt = 0;
+ if (reliable)
+ opt |= L2CAP_LM_RELIABLE;
+ if (master)
+ opt |= L2CAP_LM_MASTER;
+ if (auth)
+ opt |= L2CAP_LM_AUTH;
+ if (encrypt)
+ opt |= L2CAP_LM_ENCRYPT;
+ if (secure)
+ opt |= L2CAP_LM_SECURE;
+
+ if (opt && setsockopt(sk, SOL_L2CAP, L2CAP_LM, &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't set L2CAP link mode: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Get default options */
+ memset(&opts, 0, sizeof(opts));
+ optlen = sizeof(opts);
+
+ if (getsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get default L2CAP options: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Set new options */
+ opts.omtu = omtu;
+ opts.imtu = imtu;
+ if (rfcmode > 0)
+ opts.mode = rfcmode;
+
+ if (setsockopt(sk, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) {
+ syslog(LOG_ERR, "Can't set L2CAP options: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ if (socktype == SOCK_DGRAM) {
+ handler(sk);
+ return;
+ }
+
+ /* Listen for connections */
+ if (listen(sk, 10)) {
+ syslog(LOG_ERR, "Can not listen on the socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Check for socket address */
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ psm = btohs(addr.l2_psm);
+
+ syslog(LOG_INFO, "Waiting for connection on psm %d ...", psm);
+
+ while(1) {
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0) {
+ syslog(LOG_ERR, "Accept failed: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ if (fork()) {
+ /* Parent */
+ close(nsk);
+ continue;
+ }
+ /* Child */
+ close(sk);
+
+ /* Get current options */
+ memset(&opts, 0, sizeof(opts));
+ optlen = sizeof(opts);
+
+ if (getsockopt(nsk, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get L2CAP options: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+
+ /* Get connection information */
+ memset(&conn, 0, sizeof(conn));
+ optlen = sizeof(conn);
+
+ if (getsockopt(nsk, SOL_L2CAP, L2CAP_CONNINFO, &conn, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get L2CAP connection information: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+
+ ba2str(&addr.l2_bdaddr, ba);
+ syslog(LOG_INFO, "Connect from %s [imtu %d, omtu %d, flush_to %d, "
+ "mode %d, handle %d, class 0x%02x%02x%02x]",
+ ba, opts.imtu, opts.omtu, opts.flush_to, opts.mode, conn.hci_handle,
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+#if 0
+ /* Enable SO_TIMESTAMP */
+ if (timestamp) {
+ int t = 1;
+
+ if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+#endif
+
+ /* Enable SO_LINGER */
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+ }
+
+ handler(nsk);
+
+ syslog(LOG_INFO, "Disconnect: %m");
+ exit(0);
+ }
+
+ return;
+
+error:
+ close(sk);
+ exit(1);
+}
+
+static void dump_mode(int sk)
+{
+ socklen_t optlen;
+ int opt, len;
+
+ syslog(LOG_INFO, "Receiving ...");
+ while (1) {
+ fd_set rset;
+
+ FD_ZERO(&rset);
+ FD_SET(sk, &rset);
+
+ if (select(sk + 1, &rset, NULL, NULL, NULL) < 0)
+ return;
+
+ if (!FD_ISSET(sk, &rset))
+ continue;
+
+ len = read(sk, buf, data_size);
+ if (len <= 0) {
+ if (len < 0) {
+ if (reliable && (errno == ECOMM)) {
+ syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.");
+ optlen = sizeof(opt);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
+ syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
+ strerror(errno), errno);
+ return;
+ }
+ continue;
+ } else {
+ syslog(LOG_ERR, "Read error: %s(%d)",
+ strerror(errno), errno);
+ }
+ }
+ return;
+ }
+
+ syslog(LOG_INFO, "Recevied %d bytes", len);
+ hexdump(buf, len);
+ }
+}
+
+static void recv_mode(int sk)
+{
+ struct timeval tv_beg, tv_end, tv_diff;
+ struct pollfd p;
+ char ts[30];
+ long total;
+ uint32_t seq;
+ socklen_t optlen;
+ int opt;
+
+ syslog(LOG_INFO, "Receiving ...");
+
+ memset(ts, 0, sizeof(ts));
+
+ p.fd = sk;
+ p.events = POLLIN | POLLERR | POLLHUP;
+
+ seq = 0;
+ while (1) {
+ gettimeofday(&tv_beg, NULL);
+ total = 0;
+ while (total < data_size) {
+ uint32_t sq;
+ uint16_t l;
+ int i, len;
+
+ p.revents = 0;
+ if (poll(&p, 1, -1) <= 0)
+ return;
+
+ if (p.revents & (POLLERR | POLLHUP))
+ return;
+
+ len = recv(sk, buf, data_size, 0);
+ if (len < 0) {
+ if (reliable && (errno == ECOMM)) {
+ syslog(LOG_INFO, "L2CAP Error ECOMM - clearing error and continuing.\n");
+ optlen = sizeof(opt);
+ if (getsockopt(sk, SOL_SOCKET, SO_ERROR, &opt, &optlen) < 0) {
+ syslog(LOG_ERR, "Couldn't getsockopt(SO_ERROR): %s (%d)",
+ strerror(errno), errno);
+ return;
+ }
+ continue;
+ } else {
+ syslog(LOG_ERR, "Read failed: %s (%d)",
+ strerror(errno), errno);
+ }
+ }
+
+ if (len < 6)
+ break;
+
+ if (timestamp) {
+ struct timeval tv;
+
+ if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
+ timestamp = 0;
+ memset(ts, 0, sizeof(ts));
+ } else {
+ sprintf(ts, "[%ld.%ld] ",
+ tv.tv_sec, tv.tv_usec);
+ }
+ }
+
+ /* 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 (len != l) {
+ syslog(LOG_INFO, "size missmatch: %d -> %d", len, l);
+ continue;
+ }
+
+ /* Verify data */
+ for (i = 6; i < len; i++) {
+ if (buf[i] != 0x7f)
+ syslog(LOG_INFO, "data missmatch: byte %d 0x%2.2x", i, buf[i]);
+ }
+
+ total += len;
+ }
+ gettimeofday(&tv_end, NULL);
+
+ timersub(&tv_end, &tv_beg, &tv_diff);
+
+ syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
+ tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+ }
+}
+
+static void do_send(int sk)
+{
+ uint32_t seq;
+ int i, fd, len;
+
+ syslog(LOG_INFO, "Sending ...");
+
+ if (filename) {
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Open failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+ len = read(fd, buf, data_size);
+ send(sk, buf, len, 0);
+ return;
+ } else {
+ for (i = 6; i < data_size; i++)
+ buf[i] = 0x7f;
+ }
+
+ seq = 0;
+ while ((num_frames == -1) || (num_frames-- > 0)) {
+ *(uint32_t *) buf = htobl(seq);
+ *(uint16_t *) (buf + 4) = htobs(data_size);
+ seq++;
+
+ len = send(sk, buf, data_size, 0);
+ if (len < 0 || len != data_size) {
+ syslog(LOG_ERR, "Send failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (num_frames && delay && count && !(seq % count))
+ usleep(delay);
+ }
+}
+
+static void send_mode(int sk)
+{
+ do_send(sk);
+
+ syslog(LOG_INFO, "Closing channel ...");
+ if (shutdown(sk, SHUT_RDWR) < 0)
+ syslog(LOG_INFO, "Close failed: %m");
+ else
+ syslog(LOG_INFO, "Done");
+}
+
+static void senddump_mode(int sk)
+{
+ do_send(sk);
+
+ dump_mode(sk);
+}
+
+static void reconnect_mode(char *svr)
+{
+ while (1) {
+ int sk = do_connect(svr);
+ close(sk);
+ }
+}
+
+static void connect_mode(char *svr)
+{
+ struct pollfd p;
+ int sk;
+
+ if ((sk = do_connect(svr)) < 0)
+ exit(1);
+
+ p.fd = sk;
+ p.events = POLLERR | POLLHUP;
+
+ while (1) {
+ p.revents = 0;
+ if (poll(&p, 1, 500))
+ break;
+ }
+
+ syslog(LOG_INFO, "Disconnected");
+
+ close(sk);
+}
+
+static void multi_connect_mode(int argc, char *argv[])
+{
+ int i, n, sk;
+
+ while (1) {
+ for (n = 0; n < argc; n++) {
+ for (i = 0; i < count; i++) {
+ if (fork())
+ continue;
+
+ /* Child */
+ sk = do_connect(argv[n]);
+ usleep(500);
+ close(sk);
+ exit(0);
+ }
+ }
+ sleep(4);
+ }
+}
+
+static void info_request(char *svr)
+{
+ unsigned char buf[48];
+ l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
+ l2cap_info_req *req = (l2cap_info_req *) (buf + L2CAP_CMD_HDR_SIZE);
+ l2cap_info_rsp *rsp = (l2cap_info_rsp *) (buf + L2CAP_CMD_HDR_SIZE);
+ uint16_t mtu;
+ uint32_t mask;
+ struct sockaddr_l2 addr;
+ int sk, err;
+
+ sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, &bdaddr);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto failed;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(svr, &addr.l2_bdaddr);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+ perror("Can't connect socket");
+ goto failed;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ cmd->code = L2CAP_INFO_REQ;
+ cmd->ident = 42;
+ cmd->len = htobs(2);
+ req->type = htobs(0x0001);
+
+ if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+ perror("Can't send info request");
+ goto failed;
+ }
+
+ err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 2, 0);
+ if (err < 0) {
+ perror("Can't receive info response");
+ goto failed;
+ }
+
+ switch (btohs(rsp->result)) {
+ case 0x0000:
+ mtu = btohs(bt_get_unaligned((uint16_t *) rsp->data));
+ printf("Connectionless MTU size is %d\n", mtu);
+ break;
+ case 0x0001:
+ printf("Connectionless MTU is not supported\n");
+ break;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ cmd->code = L2CAP_INFO_REQ;
+ cmd->ident = 42;
+ cmd->len = htobs(2);
+ req->type = htobs(0x0002);
+
+ if (send(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_REQ_SIZE, 0) < 0) {
+ perror("Can't send info request");
+ goto failed;
+ }
+
+ err = recv(sk, buf, L2CAP_CMD_HDR_SIZE + L2CAP_INFO_RSP_SIZE + 4, 0);
+ if (err < 0) {
+ perror("Can't receive info response");
+ goto failed;
+ }
+
+ switch (btohs(rsp->result)) {
+ case 0x0000:
+ mask = btohl(bt_get_unaligned((uint32_t *) rsp->data));
+ printf("Extended feature mask is 0x%04x\n", mask);
+ if (mask & 0x01)
+ printf(" Flow control mode\n");
+ if (mask & 0x02)
+ printf(" Retransmission mode\n");
+ if (mask & 0x04)
+ printf(" Bi-directional QoS\n");
+ break;
+ case 0x0001:
+ printf("Extended feature mask is not supported\n");
+ break;
+ }
+
+failed:
+ close(sk);
+}
+
+static void usage(void)
+{
+ printf("l2test - L2CAP testing\n"
+ "Usage:\n");
+ printf("\tl2test <mode> [options] [bdaddr]\n");
+ printf("Modes:\n"
+ "\t-r listen and receive\n"
+ "\t-w listen and send\n"
+ "\t-d listen and dump incoming data\n"
+ "\t-x listen, then send, then dump incoming data\n"
+ "\t-s connect and send\n"
+ "\t-u connect and receive\n"
+ "\t-n connect and be silent\n"
+ "\t-y connect, then send, then dump incoming data\n"
+ "\t-c connect, disconnect, connect, ...\n"
+ "\t-m multiple connects\n"
+ "\t-z information request\n");
+
+ printf("Options:\n"
+ "\t[-b bytes] [-i device] [-P psm]\n"
+ "\t[-I imtu] [-O omtu]\n"
+ "\t[-L seconds] enable SO_LINGER\n"
+ "\t[-B filename] use data packets from file\n"
+ "\t[-N num] send num frames (default = infinite)\n"
+ "\t[-C num] send num frames before delay (default = 1)\n"
+ "\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+ "\t[-X mode] select retransmission/flow-control mode\n"
+ "\t[-R] reliable mode\n"
+ "\t[-G] use connectionless channel (datagram)\n"
+ "\t[-A] request authentication\n"
+ "\t[-E] request encryption\n"
+ "\t[-S] secure connection\n"
+ "\t[-M] become master\n"
+ "\t[-T] enable timestamps\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ int opt, sk, mode = RECV, need_addr = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt(argc,argv,"rdscuwmnxyzb:i:P:I:O:B:N:L:C:D:X:RGAESMT")) != EOF) {
+ switch(opt) {
+ case 'r':
+ mode = RECV;
+ break;
+
+ case 's':
+ mode = SEND;
+ need_addr = 1;
+ break;
+
+ case 'w':
+ mode = LSEND;
+ break;
+
+ case 'u':
+ mode = CRECV;
+ need_addr = 1;
+ break;
+
+ case 'd':
+ mode = DUMP;
+ break;
+
+ case 'c':
+ mode = RECONNECT;
+ need_addr = 1;
+ break;
+
+ case 'n':
+ mode = CONNECT;
+ need_addr = 1;
+ break;
+
+ case 'm':
+ mode = MULTY;
+ need_addr = 1;
+ break;
+
+ case 'x':
+ mode = LSENDDUMP;
+ break;
+
+ case 'y':
+ mode = SENDDUMP;
+ break;
+
+ case 'z':
+ mode = INFOREQ;
+ need_addr = 1;
+ break;
+
+ case 'b':
+ data_size = atoi(optarg);
+ break;
+
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'P':
+ psm = atoi(optarg);
+ break;
+
+ case 'I':
+ imtu = atoi(optarg);
+ break;
+
+ case 'O':
+ omtu = atoi(optarg);
+ break;
+
+ case 'L':
+ linger = atoi(optarg);
+ break;
+
+ case 'B':
+ filename = strdup(optarg);
+ break;
+
+ case 'N':
+ num_frames = atoi(optarg);
+ break;
+
+ case 'C':
+ count = atoi(optarg);
+ break;
+
+ case 'D':
+ delay = atoi(optarg) * 1000;
+ break;
+
+ case 'X':
+ rfcmode = atoi(optarg);
+ break;
+
+ case 'R':
+ reliable = 1;
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encrypt = 1;
+ break;
+
+ case 'S':
+ secure = 1;
+ break;
+
+ case 'G':
+ socktype = SOCK_DGRAM;
+ break;
+
+ case 'T':
+ timestamp = 1;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (need_addr && !(argc - optind)) {
+ usage();
+ exit(1);
+ }
+
+ if (data_size < 0) {
+ data_size = 48;
+ if (imtu > data_size)
+ data_size = imtu;
+ if (omtu > data_size)
+ data_size = omtu;
+ }
+
+ 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 CRECV:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ recv_mode(sk);
+ break;
+
+ case DUMP:
+ do_listen(dump_mode);
+ break;
+
+ case SEND:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ send_mode(sk);
+ break;
+
+ case LSEND:
+ do_listen(send_mode);
+ break;
+
+ case RECONNECT:
+ reconnect_mode(argv[optind]);
+ break;
+
+ case MULTY:
+ multi_connect_mode(argc - optind, argv + optind);
+ break;
+
+ case CONNECT:
+ connect_mode(argv[optind]);
+ break;
+
+ case SENDDUMP:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ senddump_mode(sk);
+ break;
+
+ case LSENDDUMP:
+ do_listen(senddump_mode);
+ break;
+
+ case INFOREQ:
+ info_request(argv[optind]);
+ exit(0);
+ }
+
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}
diff --git a/test/lmptest.c b/test/lmptest.c
new file mode 100644
index 00000000..276ca071
--- /dev/null
+++ b/test/lmptest.c
@@ -0,0 +1,175 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#if 0
+#define OCF_ERICSSON_SEND_LMP 0x0021
+typedef struct {
+ uint16_t handle;
+ uint8_t length;
+ uint8_t data[17];
+} __attribute__ ((packed)) ericsson_send_lmp_cp;
+#define ERICSSON_SEND_LMP_CP_SIZE 20
+
+static int ericsson_send_lmp(int dd, uint16_t handle, uint8_t length, uint8_t *data)
+{
+ struct hci_request rq;
+ ericsson_send_lmp_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.handle = htobs(handle);
+ cp.length = length;
+ memcpy(cp.data, data, length);
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_ERICSSON_SEND_LMP;
+ rq.cparam = &cp;
+ rq.clen = ERICSSON_SEND_LMP_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+#endif
+
+#define OCF_ERICSSON_WRITE_EVENTS 0x0043
+typedef struct {
+ uint8_t mask;
+ uint8_t opcode;
+ uint8_t opcode_ext;
+} __attribute__ ((packed)) ericsson_write_events_cp;
+#define ERICSSON_WRITE_EVENTS_CP_SIZE 3
+
+static int ericsson_write_events(int dd, uint8_t mask)
+{
+ struct hci_request rq;
+ ericsson_write_events_cp cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.mask = mask;
+ cp.opcode = 0x00;
+ cp.opcode_ext = 0x00;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = OCF_ERICSSON_WRITE_EVENTS;
+ rq.cparam = &cp;
+ rq.clen = ERICSSON_WRITE_EVENTS_CP_SIZE;
+ rq.rparam = NULL;
+ rq.rlen = 0;
+
+ if (hci_send_req(dd, &rq, 1000) < 0)
+ return -1;
+
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("lmptest - Utility for testing special LMP functions\n\n");
+ printf("Usage:\n"
+ "\tlmptest [-i <dev>]\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct hci_version ver;
+ int dd, opt, dev = 0;
+
+ while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ dev = hci_devid(optarg);
+ if (dev < 0) {
+ perror("Invalid device");
+ exit(1);
+ }
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ dd = hci_open_dev(dev);
+ if (dd < 0) {
+ fprintf(stderr, "Can't open device hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (hci_read_local_version(dd, &ver, 1000) < 0) {
+ fprintf(stderr, "Can't read version for hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ exit(1);
+ }
+
+ if (ver.manufacturer != 37 && ver.manufacturer != 48) {
+ fprintf(stderr, "Can't find supported device hci%d: %s (%d)\n",
+ dev, strerror(ENOSYS), ENOSYS);
+ hci_close_dev(dd);
+ exit(1);
+ }
+
+ if (ericsson_write_events(dd, 0x03) < 0) {
+ fprintf(stderr, "Can't activate events for hci%d: %s (%d)\n",
+ dev, strerror(errno), errno);
+ hci_close_dev(dd);
+ exit(1);
+ }
+
+ hci_close_dev(dd);
+
+ return 0;
+}
diff --git a/test/passkey-agent.c b/test/passkey-agent.c
new file mode 100644
index 00000000..8ac91d27
--- /dev/null
+++ b/test/passkey-agent.c
@@ -0,0 +1,418 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2004-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <getopt.h>
+#include <string.h>
+
+#include <dbus/dbus.h>
+
+#define INTERFACE "org.bluez.Security"
+
+static char *passkey = NULL;
+static char *address = NULL;
+
+static int do_reject = 0;
+
+static volatile sig_atomic_t __io_canceled = 0;
+static volatile sig_atomic_t __io_terminated = 0;
+
+static void sig_term(int sig)
+{
+ __io_canceled = 1;
+}
+
+static DBusHandlerResult agent_filter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ const char *name, *old, *new;
+
+ if (!dbus_message_is_signal(msg, DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &name, DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new, DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!strcmp(name, "org.bluez") && *new == '\0') {
+ fprintf(stderr, "Passkey service has been terminated\n");
+ __io_terminated = 1;
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static DBusHandlerResult request_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *path, *address;
+ dbus_bool_t numeric;
+
+ if (!passkey)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &path, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_BOOLEAN, &numeric, DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for passkey Request method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (do_reject) {
+ reply = dbus_message_new_error(msg,
+ "org.bluez.Error.Rejected", "");
+ goto send;
+ }
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ fprintf(stderr, "Can't create reply message\n");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ printf("Passkey request for device %s\n", address);
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &passkey,
+ DBUS_TYPE_INVALID);
+
+send:
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult cancel_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+ const char *path, *address;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &path, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for passkey Confirm method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ printf("Request canceled for device %s\n", address);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ fprintf(stderr, "Can't create reply message\n");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult release_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) {
+ fprintf(stderr, "Invalid arguments for passkey Release method");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ }
+
+ if (!__io_canceled)
+ fprintf(stderr, "Passkey service has been released\n");
+
+ __io_terminated = 1;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply) {
+ fprintf(stderr, "Can't create reply message\n");
+ return DBUS_HANDLER_RESULT_NEED_MEMORY;
+ }
+
+ dbus_connection_send(conn, reply, NULL);
+
+ dbus_connection_flush(conn);
+
+ dbus_message_unref(reply);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult agent_message(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Request"))
+ return request_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Cancel"))
+ return cancel_message(conn, msg, data);
+
+ if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release"))
+ return release_message(conn, msg, data);
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static const DBusObjectPathVTable agent_table = {
+ .message_function = agent_message,
+};
+
+static int register_agent(DBusConnection *conn, const char *agent_path,
+ const char *remote_address, int use_default)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *path, *method, *address = remote_address;
+
+ if (!dbus_connection_register_object_path(conn, agent_path,
+ &agent_table, NULL)) {
+ fprintf(stderr, "Can't register object path for agent\n");
+ return -1;
+ }
+
+ if (use_default) {
+ path = "/org/bluez";
+ method = "RegisterDefaultPasskeyAgent";
+ } else {
+ path = "/org/bluez/hci0";
+ method = "RegisterPasskeyAgent";
+ }
+
+ msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ return -1;
+ }
+
+ if (use_default)
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
+ DBUS_TYPE_INVALID);
+ else
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
+ DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't register passkey agent\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return -1;
+ }
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ return 0;
+}
+
+static int unregister_agent(DBusConnection *conn, const char *agent_path,
+ const char *remote_address, int use_default)
+{
+ DBusMessage *msg, *reply;
+ DBusError err;
+ const char *path, *method, *address = remote_address;
+
+ if (use_default) {
+ path = "/org/bluez";
+ method = "UnregisterDefaultPasskeyAgent";
+ } else {
+ path = "/org/bluez/hci0";
+ method = "UnregisterPasskeyAgent";
+ }
+
+ msg = dbus_message_new_method_call("org.bluez", path, INTERFACE, method);
+ if (!msg) {
+ fprintf(stderr, "Can't allocate new method call\n");
+ dbus_connection_unref(conn);
+ exit(1);
+ }
+
+ if (use_default)
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
+ DBUS_TYPE_INVALID);
+ else
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &agent_path,
+ DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
+
+ dbus_message_unref(msg);
+
+ if (!reply) {
+ fprintf(stderr, "Can't unregister passkey agent\n");
+ if (dbus_error_is_set(&err)) {
+ fprintf(stderr, "%s\n", err.message);
+ dbus_error_free(&err);
+ }
+ return -1;
+ }
+
+ dbus_message_unref(reply);
+
+ dbus_connection_flush(conn);
+
+ dbus_connection_unregister_object_path(conn, agent_path);
+
+ return 0;
+}
+
+static void usage(void)
+{
+ printf("Bluetooth passkey agent ver %s\n\n", VERSION);
+
+ printf("Usage:\n"
+ "\tpasskey-agent [--default] [--path agent-path] <passkey> [address]\n"
+ "\n");
+}
+
+static struct option main_options[] = {
+ { "default", 0, 0, 'd' },
+ { "reject", 0, 0, 'r' },
+ { "path", 1, 0, 'p' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ DBusConnection *conn;
+ char match_string[128], default_path[128], *agent_path = NULL;
+ int opt, use_default = 0;
+
+ snprintf(default_path, sizeof(default_path),
+ "/org/bluez/passkey_agent_%d", getpid());
+
+ while ((opt = getopt_long(argc, argv, "+dp:h", main_options, NULL)) != EOF) {
+ switch(opt) {
+ case 'd':
+ use_default = 1;
+ break;
+ case 'r':
+ do_reject = 1;
+ break;
+ case 'p':
+ if (optarg[0] != '/') {
+ fprintf(stderr, "Invalid path\n");
+ exit(1);
+ }
+ agent_path = strdup(optarg);
+ break;
+ case 'h':
+ usage();
+ exit(0);
+ default:
+ exit(1);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ passkey = strdup(argv[0]);
+ address = (argc > 1) ? strdup(argv[1]) : NULL;
+
+ if (!use_default && !address) {
+ usage();
+ exit(1);
+ }
+
+ if (!agent_path)
+ agent_path = strdup(default_path);
+
+ conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
+ if (!conn) {
+ fprintf(stderr, "Can't get on system bus");
+ exit(1);
+ }
+
+ if (register_agent(conn, agent_path, address, use_default) < 0) {
+ dbus_connection_unref(conn);
+ exit(1);
+ }
+
+ if (!dbus_connection_add_filter(conn, agent_filter, NULL, NULL))
+ fprintf(stderr, "Can't add signal filter");
+
+ snprintf(match_string, sizeof(match_string),
+ "interface=%s,member=NameOwnerChanged,arg0=%s",
+ DBUS_INTERFACE_DBUS, "org.bluez");
+
+ dbus_bus_add_match(conn, match_string, NULL);
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDSTOP;
+ sa.sa_handler = sig_term;
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+
+ while (!__io_canceled && !__io_terminated) {
+ if (dbus_connection_read_write_dispatch(conn, 500) != TRUE)
+ break;
+ }
+
+ if (!__io_terminated)
+ unregister_agent(conn, agent_path, address, use_default);
+
+ if (passkey)
+ free(passkey);
+
+ dbus_connection_unref(conn);
+
+ return 0;
+}
diff --git a/test/rctest.c b/test/rctest.c
new file mode 100644
index 00000000..08620d4e
--- /dev/null
+++ b/test/rctest.c
@@ -0,0 +1,688 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/rfcomm.h>
+
+/* Test modes */
+enum {
+ SEND,
+ RECV,
+ RECONNECT,
+ MULTY,
+ DUMP,
+ CONNECT,
+ CRECV,
+ LSEND
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 127;
+static long num_frames = -1;
+
+/* Default number of consecutive frames before the delay */
+static int count = 1;
+
+/* Default delay after sending count number of frames */
+static unsigned long delay = 0;
+
+/* Default addr and channel */
+static bdaddr_t bdaddr;
+static uint8_t channel = 10;
+
+static char *filename = NULL;
+
+static int master = 0;
+static int auth = 0;
+static int encrypt = 0;
+static int secure = 0;
+static int socktype = SOCK_STREAM;
+static int linger = 0;
+static int timestamp = 0;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static int do_connect(char *svr)
+{
+ struct sockaddr_rc addr;
+ struct rfcomm_conninfo conn;
+ socklen_t optlen;
+ int sk, opt;
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Bind to local address */
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, &bdaddr);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+#if 0
+ /* Enable SO_TIMESTAMP */
+ if (timestamp) {
+ int t = 1;
+
+ if (setsockopt(sk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+#endif
+
+ /* Enable SO_LINGER */
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(sk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+
+ /* Set link mode */
+ opt = 0;
+ if (master)
+ opt |= RFCOMM_LM_MASTER;
+ if (auth)
+ opt |= RFCOMM_LM_AUTH;
+ if (encrypt)
+ opt |= RFCOMM_LM_ENCRYPT;
+ if (secure)
+ opt |= RFCOMM_LM_SECURE;
+
+ if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Connect to remote device */
+ memset(&addr, 0, sizeof(addr));
+ addr.rc_family = AF_BLUETOOTH;
+ str2ba(svr, &addr.rc_bdaddr);
+ addr.rc_channel = channel;
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't connect: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Get connection information */
+ memset(&conn, 0, sizeof(conn));
+ optlen = sizeof(conn);
+
+ if (getsockopt(sk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
+ strerror(errno), errno);
+ //goto error;
+ }
+
+ syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
+ conn.hci_handle,
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+ return sk;
+
+error:
+ close(sk);
+ return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+ struct sockaddr_rc addr;
+ struct rfcomm_conninfo conn;
+ socklen_t optlen;
+ int sk, nsk, opt;
+ char ba[18];
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, socktype, BTPROTO_RFCOMM);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Bind to local address */
+ addr.rc_family = AF_BLUETOOTH;
+ bacpy(&addr.rc_bdaddr, &bdaddr);
+ addr.rc_channel = channel;
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Set link mode */
+ opt = 0;
+ if (master)
+ opt |= RFCOMM_LM_MASTER;
+ if (auth)
+ opt |= RFCOMM_LM_AUTH;
+ if (encrypt)
+ opt |= RFCOMM_LM_ENCRYPT;
+ if (secure)
+ opt |= RFCOMM_LM_SECURE;
+
+ if (opt && setsockopt(sk, SOL_RFCOMM, RFCOMM_LM, &opt, sizeof(opt)) < 0) {
+ syslog(LOG_ERR, "Can't set RFCOMM link mode: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Listen for connections */
+ if (listen(sk, 10)) {
+ syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Check for socket address */
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ if (getsockname(sk, (struct sockaddr *) &addr, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get socket name: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ channel = addr.rc_channel;
+
+ syslog(LOG_INFO, "Waiting for connection on channel %d ...", channel);
+
+ while(1) {
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0) {
+ syslog(LOG_ERR,"Accept failed: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ if (fork()) {
+ /* Parent */
+ close(nsk);
+ continue;
+ }
+ /* Child */
+ close(sk);
+
+ /* Get connection information */
+ memset(&conn, 0, sizeof(conn));
+ optlen = sizeof(conn);
+
+ if (getsockopt(nsk, SOL_RFCOMM, RFCOMM_CONNINFO, &conn, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get RFCOMM connection information: %s (%d)",
+ strerror(errno), errno);
+ //close(nsk);
+ //goto error;
+ }
+
+ ba2str(&addr.rc_bdaddr, ba);
+ syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
+ ba, conn.hci_handle,
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+#if 0
+ /* Enable SO_TIMESTAMP */
+ if (timestamp) {
+ int t = 1;
+
+ if (setsockopt(nsk, SOL_SOCKET, SO_TIMESTAMP, &t, sizeof(t)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_TIMESTAMP: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ }
+#endif
+
+ /* Enable SO_LINGER */
+ if (linger) {
+ struct linger l = { .l_onoff = 1, .l_linger = linger };
+
+ if (setsockopt(nsk, SOL_SOCKET, SO_LINGER, &l, sizeof(l)) < 0) {
+ syslog(LOG_ERR, "Can't enable SO_LINGER: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+ }
+
+ handler(nsk);
+
+ syslog(LOG_INFO, "Disconnect: %m");
+ exit(0);
+ }
+
+ return;
+
+error:
+ close(sk);
+ exit(1);
+}
+
+static void dump_mode(int sk)
+{
+ int len;
+
+ syslog(LOG_INFO, "Receiving ...");
+ while ((len = read(sk, buf, data_size)) > 0)
+ syslog(LOG_INFO, "Recevied %d bytes", len);
+}
+
+static void recv_mode(int sk)
+{
+ struct timeval tv_beg, tv_end, tv_diff;
+ char ts[30];
+ long total;
+ uint32_t seq;
+
+ syslog(LOG_INFO, "Receiving ...");
+
+ memset(ts, 0, sizeof(ts));
+
+ seq = 0;
+ while (1) {
+ gettimeofday(&tv_beg,NULL);
+ total = 0;
+ while (total < data_size) {
+ //uint32_t sq;
+ //uint16_t l;
+ int r;
+
+ if ((r = recv(sk, buf, data_size, 0)) <= 0) {
+ if (r < 0)
+ syslog(LOG_ERR, "Read failed: %s (%d)",
+ strerror(errno), errno);
+ return;
+ }
+
+ if (timestamp) {
+ struct timeval tv;
+
+ if (ioctl(sk, SIOCGSTAMP, &tv) < 0) {
+ timestamp = 0;
+ memset(ts, 0, sizeof(ts));
+ } else {
+ sprintf(ts, "[%ld.%ld] ",
+ tv.tv_sec, tv.tv_usec);
+ }
+ }
+
+#if 0
+ /* 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]);
+ }
+#endif
+ total += r;
+ }
+ gettimeofday(&tv_end,NULL);
+
+ timersub(&tv_end,&tv_beg,&tv_diff);
+
+ syslog(LOG_INFO,"%s%ld bytes in %.2f sec, %.2f kB/s", ts, total,
+ tv2fl(tv_diff), (float)(total / tv2fl(tv_diff) ) / 1024.0);
+ }
+}
+
+static void do_send(int sk)
+{
+ uint32_t seq;
+ int i, fd, len;
+
+ syslog(LOG_INFO,"Sending ...");
+
+ if (filename) {
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Open failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+ len = read(fd, buf, data_size);
+ send(sk, buf, len, 0);
+ return;
+ } else {
+ for (i = 6; i < data_size; i++)
+ buf[i] = 0x7f;
+ }
+
+ seq = 0;
+ while ((num_frames == -1) || (num_frames-- > 0)) {
+ *(uint32_t *) buf = htobl(seq);
+ *(uint16_t *) (buf + 4) = htobs(data_size);
+ seq++;
+
+ if (send(sk, buf, data_size, 0) <= 0) {
+ syslog(LOG_ERR, "Send failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (num_frames && delay && count && !(seq % count))
+ usleep(delay);
+ }
+}
+
+static void send_mode(int sk)
+{
+ do_send(sk);
+
+ syslog(LOG_INFO, "Closing channel ...");
+ if (shutdown(sk, SHUT_RDWR) < 0)
+ syslog(LOG_INFO, "Close failed: %m");
+ else
+ syslog(LOG_INFO, "Done");
+}
+
+static void reconnect_mode(char *svr)
+{
+ while(1) {
+ int sk = do_connect(svr);
+ close(sk);
+ }
+}
+
+static void multi_connect_mode(int argc, char *argv[])
+{
+ int i, n, sk;
+
+ while (1) {
+ for (n = 0; n < argc; n++) {
+ for (i = 0; i < count; i++) {
+ if (fork())
+ continue;
+
+ /* Child */
+ sk = do_connect(argv[n]);
+ usleep(500);
+ close(sk);
+ exit(0);
+ }
+ }
+ sleep(4);
+ }
+}
+
+static void usage(void)
+{
+ printf("rctest - RFCOMM testing\n"
+ "Usage:\n");
+ printf("\trctest <mode> [options] [bdaddr]\n");
+ printf("Modes:\n"
+ "\t-r listen and receive\n"
+ "\t-w listen and send\n"
+ "\t-d listen and dump incoming data\n"
+ "\t-s connect and send\n"
+ "\t-u connect and receive\n"
+ "\t-n connect and be silent\n"
+ "\t-c connect, disconnect, connect, ...\n"
+ "\t-m multiple connects\n");
+
+ printf("Options:\n"
+ "\t[-b bytes] [-i device] [-P channel]\n"
+ "\t[-L seconds] enabled SO_LINGER option\n"
+ "\t[-B filename] use data packets from file\n"
+ "\t[-N num] number of frames to send\n"
+ "\t[-C num] send num frames before delay (default = 1)\n"
+ "\t[-D milliseconds] delay after sending num frames (default = 0)\n"
+ "\t[-A] request authentication\n"
+ "\t[-E] request encryption\n"
+ "\t[-S] secure connection\n"
+ "\t[-M] become master\n"
+ "\t[-T] enable timestamps\n");
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa;
+ int opt, sk, mode = RECV, need_addr = 0;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ while ((opt=getopt(argc,argv,"rdscuwmnb:i:P:B:N:MAESL:C:D:T")) != EOF) {
+ switch (opt) {
+ case 'r':
+ mode = RECV;
+ break;
+
+ case 's':
+ mode = SEND;
+ need_addr = 1;
+ break;
+
+ case 'w':
+ mode = LSEND;
+ break;
+
+ case 'u':
+ mode = CRECV;
+ need_addr = 1;
+ break;
+
+ case 'd':
+ mode = DUMP;
+ break;
+
+ case 'c':
+ mode = RECONNECT;
+ need_addr = 1;
+ break;
+
+ case 'n':
+ mode = CONNECT;
+ need_addr = 1;
+ break;
+
+ case 'm':
+ mode = MULTY;
+ need_addr = 1;
+ break;
+
+ case 'b':
+ data_size = atoi(optarg);
+ break;
+
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &bdaddr);
+ else
+ str2ba(optarg, &bdaddr);
+ break;
+
+ case 'P':
+ channel = atoi(optarg);
+ break;
+
+ case 'M':
+ master = 1;
+ break;
+
+ case 'A':
+ auth = 1;
+ break;
+
+ case 'E':
+ encrypt = 1;
+ break;
+
+ case 'S':
+ secure = 1;
+ break;
+
+ case 'L':
+ linger = atoi(optarg);
+ break;
+
+ case 'B':
+ filename = strdup(optarg);
+ break;
+
+ case 'N':
+ num_frames = atoi(optarg);
+ break;
+
+ case 'C':
+ count = atoi(optarg);
+ break;
+
+ case 'D':
+ delay = atoi(optarg) * 1000;
+ break;
+
+ case 'T':
+ timestamp = 1;
+ break;
+
+ default:
+ usage();
+ exit(1);
+ }
+ }
+
+ if (need_addr && !(argc - optind)) {
+ 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("rctest", LOG_PERROR | LOG_PID, LOG_LOCAL0);
+
+ switch (mode) {
+ case RECV:
+ do_listen(recv_mode);
+ break;
+
+ case CRECV:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ recv_mode(sk);
+ break;
+
+ case DUMP:
+ do_listen(dump_mode);
+ break;
+
+ case SEND:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ send_mode(sk);
+ break;
+
+ case LSEND:
+ do_listen(send_mode);
+ break;
+
+ case RECONNECT:
+ reconnect_mode(argv[optind]);
+ break;
+
+ case MULTY:
+ multi_connect_mode(argc - optind, argv + optind);
+ break;
+
+ case CONNECT:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ dump_mode(sk);
+ break;
+ }
+
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}
diff --git a/test/scotest.c b/test/scotest.c
new file mode 100644
index 00000000..4c5a310c
--- /dev/null
+++ b/test/scotest.c
@@ -0,0 +1,434 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2002-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sco.h>
+
+/* Test modes */
+enum {
+ SEND,
+ RECV,
+ RECONNECT,
+ MULTY,
+ DUMP,
+ CONNECT
+};
+
+static unsigned char *buf;
+
+/* Default data size */
+static long data_size = 672;
+
+static bdaddr_t bdaddr;
+
+static float tv2fl(struct timeval tv)
+{
+ return (float)tv.tv_sec + (float)(tv.tv_usec/1000000.0);
+}
+
+static int do_connect(char *svr)
+{
+ struct sockaddr_sco addr;
+ struct sco_conninfo conn;
+ socklen_t optlen;
+ int sk;
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Bind to local address */
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &bdaddr);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Connect to remote device */
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ str2ba(svr, &addr.sco_bdaddr);
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't connect: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Get connection information */
+ memset(&conn, 0, sizeof(conn));
+ optlen = sizeof(conn);
+
+ if (getsockopt(sk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ syslog(LOG_INFO, "Connected [handle %d, class 0x%02x%02x%02x]",
+ conn.hci_handle,
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+ return sk;
+
+error:
+ close(sk);
+ return -1;
+}
+
+static void do_listen(void (*handler)(int sk))
+{
+ struct sockaddr_sco addr;
+ struct sco_conninfo conn;
+ socklen_t optlen;
+ int sk, nsk;
+ char ba[18];
+
+ /* Create socket */
+ sk = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't create socket: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Bind to local address */
+ memset(&addr, 0, sizeof(addr));
+ addr.sco_family = AF_BLUETOOTH;
+ bacpy(&addr.sco_bdaddr, &bdaddr);
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ syslog(LOG_ERR, "Can't bind socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ /* Listen for connections */
+ if (listen(sk, 10)) {
+ syslog(LOG_ERR,"Can not listen on the socket: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+
+ syslog(LOG_INFO,"Waiting for connection ...");
+
+ while (1) {
+ memset(&addr, 0, sizeof(addr));
+ optlen = sizeof(addr);
+
+ nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+ if (nsk < 0) {
+ syslog(LOG_ERR,"Accept failed: %s (%d)",
+ strerror(errno), errno);
+ goto error;
+ }
+ if (fork()) {
+ /* Parent */
+ close(nsk);
+ continue;
+ }
+ /* Child */
+ close(sk);
+
+ /* Get connection information */
+ memset(&conn, 0, sizeof(conn));
+ optlen = sizeof(conn);
+
+ if (getsockopt(nsk, SOL_SCO, SCO_CONNINFO, &conn, &optlen) < 0) {
+ syslog(LOG_ERR, "Can't get SCO connection information: %s (%d)",
+ strerror(errno), errno);
+ close(nsk);
+ goto error;
+ }
+
+ ba2str(&addr.sco_bdaddr, ba);
+ syslog(LOG_INFO, "Connect from %s [handle %d, class 0x%02x%02x%02x]",
+ ba, conn.hci_handle,
+ conn.dev_class[2], conn.dev_class[1], conn.dev_class[0]);
+
+ handler(nsk);
+
+ syslog(LOG_INFO, "Disconnect");
+ exit(0);
+ }
+
+ return;
+
+error:
+ close(sk);
+ exit(1);
+}
+
+static void dump_mode(int sk)
+{
+ int len;
+
+ syslog(LOG_INFO,"Receiving ...");
+ while ((len = read(sk, buf, data_size)) > 0)
+ syslog(LOG_INFO, "Recevied %d bytes", len);
+}
+
+static void recv_mode(int sk)
+{
+ 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(sk, 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 );
+ }
+}
+
+static void send_mode(char *svr)
+{
+ struct sco_options so;
+ socklen_t len;
+ uint32_t seq;
+ int i, sk;
+
+ if ((sk = do_connect(svr)) < 0) {
+ syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ len = sizeof(so);
+ if (getsockopt(sk, SOL_SCO, SCO_OPTIONS, &so, &len) < 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);
+ seq++;
+
+ if (send(sk, buf, so.mtu, 0) <= 0) {
+ syslog(LOG_ERR, "Send failed: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ usleep(1);
+ }
+}
+
+static void reconnect_mode(char *svr)
+{
+ while (1) {
+ int sk;
+
+ if ((sk = do_connect(svr)) < 0) {
+ syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ close(sk);
+
+ sleep(5);
+ }
+}
+
+static void multy_connect_mode(char *svr)
+{
+ while (1) {
+ int i, sk;
+
+ for (i = 0; i < 10; i++){
+ if (fork())
+ continue;
+
+ /* Child */
+ sk = do_connect(svr);
+ if (sk < 0) {
+ syslog(LOG_ERR, "Can't connect to the server: %s (%d)",
+ strerror(errno), errno);
+ }
+ close(sk);
+ exit(0);
+ }
+
+ sleep(19);
+ }
+}
+
+static 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 connect and send (client)\n"
+ "\t-n connect and be silent (client)\n");
+}
+
+int main(int argc ,char *argv[])
+{
+ struct sigaction sa;
+ int opt, sk, mode = RECV;
+
+ while ((opt=getopt(argc,argv,"rdscmnb:")) != 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 'n':
+ mode = CONNECT;
+ 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;
+
+ case CONNECT:
+ sk = do_connect(argv[optind]);
+ if (sk < 0)
+ exit(1);
+ dump_mode(sk);
+ break;
+ }
+
+ syslog(LOG_INFO, "Exit");
+
+ closelog();
+
+ return 0;
+}
diff --git a/test/sdptest.c b/test/sdptest.c
new file mode 100644
index 00000000..d2213bfa
--- /dev/null
+++ b/test/sdptest.c
@@ -0,0 +1,146 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2005-2008 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <signal.h>
+#include <sys/socket.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/sdp_lib.h>
+
+static volatile sig_atomic_t __io_finished = 0;
+
+static void callback(uint8_t type, uint16_t status,
+ uint8_t *rsp, size_t size, void *udata)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ printf("%02x ", rsp[i]);
+ if ((i + 1) % 8 == 0)
+ printf(" ");
+ if ((i + 1) % 16 == 0)
+ printf("\n");
+ }
+ printf("\n");
+
+ __io_finished = 1;
+}
+
+static void cmd_search(bdaddr_t *src, bdaddr_t *dst)
+{
+ sdp_session_t *session;
+ sdp_list_t *search, *attrids;
+ uint32_t range = 0x0000ffff;
+ uuid_t uuid;
+
+ session = sdp_connect(src, dst, 0);
+ if (!session) {
+ perror("Can't connect to SDP service");
+ exit(1);
+ }
+
+ sdp_set_notify(session, callback, NULL);
+
+ sdp_uuid16_create(&uuid, PUBLIC_BROWSE_GROUP);
+
+ search = sdp_list_append(NULL, &uuid);
+
+ attrids = sdp_list_append(NULL, &range);
+
+ //sdp_service_search_attr_async(session, search,
+ // SDP_ATTR_REQ_RANGE, attrids);
+
+ sdp_service_search_async(session, search, 0xffff);
+
+ sdp_list_free(attrids, NULL);
+
+ sdp_list_free(search, NULL);
+
+ while (!__io_finished)
+ sdp_process(session);
+
+ sdp_close(session);
+}
+
+static void usage(void)
+{
+ printf("sdptest - Utility for SDP testing\n\n");
+ printf("Usage:\n"
+ "\tsdptest [-i <dev>] <bdaddr>\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ bdaddr_t src, dst;
+ int opt;
+
+ bacpy(&src, BDADDR_ANY);
+
+ while ((opt=getopt_long(argc, argv, "+i:h", main_options, NULL)) != -1) {
+ switch (opt) {
+ case 'i':
+ if (!strncasecmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src);
+ else
+ str2ba(optarg, &dst);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ usage();
+ exit(1);
+ }
+
+ str2ba(argv[0], &dst);
+
+ cmd_search(&src, &dst);
+
+ return 0;
+}